You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2020/08/22 13:19:45 UTC
[commons-compress] 01/01: Pack200 from Apache Harmony initially in
its own package but should be repackaged.
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch pack200
in repository https://gitbox.apache.org/repos/asf/commons-compress.git
commit 48351f2f610949b42cae1c2d818b21835bdcb0bd
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Aug 22 09:19:32 2020 -0400
Pack200 from Apache Harmony initially in its own package but should be
repackaged.
---
pom.xml | 25 +-
.../pack200/Pack200CompressorInputStream.java | 2 +-
.../pack200/Pack200CompressorOutputStream.java | 2 +-
.../compress/compressors/pack200/Pack200Utils.java | 2 +-
.../commons/compress/java/util/jar/Pack200.java | 339 ++++
.../harmony/archive/internal/nls/Messages.java | 251 +++
.../java/org/apache/harmony/pack200/Archive.java | 332 ++++
.../harmony/pack200/AttributeDefinitionBands.java | 260 ++++
.../java/org/apache/harmony/pack200/BHSDCodec.java | 516 +++++++
.../java/org/apache/harmony/pack200/BandSet.java | 788 ++++++++++
.../java/org/apache/harmony/pack200/BcBands.java | 583 +++++++
.../java/org/apache/harmony/pack200/CPClass.java | 57 +
.../org/apache/harmony/pack200/CPConstant.java | 26 +
.../java/org/apache/harmony/pack200/CPDouble.java | 37 +
.../java/org/apache/harmony/pack200/CPFloat.java | 37 +
.../java/org/apache/harmony/pack200/CPInt.java | 43 +
.../java/org/apache/harmony/pack200/CPLong.java | 48 +
.../apache/harmony/pack200/CPMethodOrField.java | 83 +
.../org/apache/harmony/pack200/CPNameAndType.java | 61 +
.../org/apache/harmony/pack200/CPSignature.java | 86 ++
.../java/org/apache/harmony/pack200/CPString.java | 44 +
.../java/org/apache/harmony/pack200/CPUTF8.java | 42 +
.../harmony/pack200/CanonicalCodecFamilies.java | 210 +++
.../org/apache/harmony/pack200/ClassBands.java | 1616 ++++++++++++++++++++
.../java/org/apache/harmony/pack200/Codec.java | 246 +++
.../org/apache/harmony/pack200/CodecEncoding.java | 346 +++++
.../apache/harmony/pack200/ConstantPoolEntry.java | 34 +
.../java/org/apache/harmony/pack200/CpBands.java | 686 +++++++++
.../java/org/apache/harmony/pack200/FileBands.java | 183 +++
.../java/org/apache/harmony/pack200/IcBands.java | 199 +++
.../java/org/apache/harmony/pack200/IntList.java | 260 ++++
.../apache/harmony/pack200/MetadataBandGroup.java | 462 ++++++
.../org/apache/harmony/pack200/NewAttribute.java | 202 +++
.../apache/harmony/pack200/NewAttributeBands.java | 965 ++++++++++++
.../org/apache/harmony/pack200/Pack200Adapter.java | 59 +
.../apache/harmony/pack200/Pack200ClassReader.java | 81 +
.../apache/harmony/pack200/Pack200Exception.java | 36 +
.../harmony/pack200/Pack200PackerAdapter.java | 102 ++
.../org/apache/harmony/pack200/PackingOptions.java | 363 +++++
.../org/apache/harmony/pack200/PackingUtils.java | 250 +++
.../apache/harmony/pack200/PopulationCodec.java | 170 ++
.../java/org/apache/harmony/pack200/RunCodec.java | 156 ++
.../java/org/apache/harmony/pack200/Segment.java | 707 +++++++++
.../org/apache/harmony/pack200/SegmentHeader.java | 360 +++++
.../java/org/apache/harmony/unpack200/Archive.java | 248 +++
.../harmony/unpack200/AttrDefinitionBands.java | 121 ++
.../apache/harmony/unpack200/AttributeLayout.java | 245 +++
.../harmony/unpack200/AttributeLayoutMap.java | 284 ++++
.../java/org/apache/harmony/unpack200/BandSet.java | 551 +++++++
.../java/org/apache/harmony/unpack200/BcBands.java | 589 +++++++
.../org/apache/harmony/unpack200/ClassBands.java | 1421 +++++++++++++++++
.../java/org/apache/harmony/unpack200/CpBands.java | 705 +++++++++
.../org/apache/harmony/unpack200/FileBands.java | 127 ++
.../org/apache/harmony/unpack200/IMatcher.java | 26 +
.../java/org/apache/harmony/unpack200/IcBands.java | 249 +++
.../java/org/apache/harmony/unpack200/IcTuple.java | 358 +++++
.../harmony/unpack200/MetadataBandGroup.java | 248 +++
.../harmony/unpack200/NewAttributeBands.java | 1050 +++++++++++++
.../harmony/unpack200/Pack200UnpackerAdapter.java | 74 +
.../java/org/apache/harmony/unpack200/Segment.java | 639 ++++++++
.../harmony/unpack200/SegmentConstantPool.java | 317 ++++
.../unpack200/SegmentConstantPoolArrayCache.java | 182 +++
.../apache/harmony/unpack200/SegmentHeader.java | 438 ++++++
.../apache/harmony/unpack200/SegmentOptions.java | 126 ++
.../org/apache/harmony/unpack200/SegmentUtils.java | 127 ++
.../bytecode/AnnotationDefaultAttribute.java | 73 +
.../unpack200/bytecode/AnnotationsAttribute.java | 197 +++
.../harmony/unpack200/bytecode/Attribute.java | 115 ++
.../unpack200/bytecode/BCIRenumberedAttribute.java | 78 +
.../harmony/unpack200/bytecode/ByteCode.java | 370 +++++
.../apache/harmony/unpack200/bytecode/CPClass.java | 96 ++
.../harmony/unpack200/bytecode/CPConstant.java | 71 +
.../unpack200/bytecode/CPConstantNumber.java | 32 +
.../harmony/unpack200/bytecode/CPDouble.java | 39 +
.../apache/harmony/unpack200/bytecode/CPField.java | 34 +
.../harmony/unpack200/bytecode/CPFieldRef.java | 98 ++
.../apache/harmony/unpack200/bytecode/CPFloat.java | 39 +
.../harmony/unpack200/bytecode/CPInteger.java | 39 +
.../unpack200/bytecode/CPInterfaceMethodRef.java | 54 +
.../apache/harmony/unpack200/bytecode/CPLong.java | 39 +
.../harmony/unpack200/bytecode/CPMember.java | 123 ++
.../harmony/unpack200/bytecode/CPMethod.java | 52 +
.../harmony/unpack200/bytecode/CPMethodRef.java | 51 +
.../harmony/unpack200/bytecode/CPNameAndType.java | 123 ++
.../apache/harmony/unpack200/bytecode/CPRef.java | 109 ++
.../harmony/unpack200/bytecode/CPString.java | 73 +
.../apache/harmony/unpack200/bytecode/CPUTF8.java | 90 ++
.../unpack200/bytecode/ClassConstantPool.java | 271 ++++
.../harmony/unpack200/bytecode/ClassFile.java | 73 +
.../harmony/unpack200/bytecode/ClassFileEntry.java | 61 +
.../harmony/unpack200/bytecode/CodeAttribute.java | 191 +++
.../unpack200/bytecode/ConstantPoolEntry.java | 83 +
.../unpack200/bytecode/ConstantValueAttribute.java | 90 ++
.../unpack200/bytecode/DeprecatedAttribute.java | 64 +
.../bytecode/EnclosingMethodAttribute.java | 91 ++
.../unpack200/bytecode/ExceptionTableEntry.java | 96 ++
.../unpack200/bytecode/ExceptionsAttribute.java | 114 ++
.../unpack200/bytecode/InnerClassesAttribute.java | 185 +++
.../bytecode/LineNumberTableAttribute.java | 91 ++
.../bytecode/LocalVariableTableAttribute.java | 167 ++
.../bytecode/LocalVariableTypeTableAttribute.java | 168 ++
.../harmony/unpack200/bytecode/NewAttribute.java | 246 +++
.../harmony/unpack200/bytecode/OperandManager.java | 238 +++
...timeVisibleorInvisibleAnnotationsAttribute.java | 84 +
...leorInvisibleParameterAnnotationsAttribute.java | 126 ++
.../unpack200/bytecode/SignatureAttribute.java | 79 +
.../unpack200/bytecode/SourceFileAttribute.java | 92 ++
.../unpack200/bytecode/forms/ByteCodeForm.java | 541 +++++++
.../harmony/unpack200/bytecode/forms/ByteForm.java | 42 +
.../unpack200/bytecode/forms/ClassRefForm.java | 71 +
.../bytecode/forms/ClassSpecificReferenceForm.java | 55 +
.../unpack200/bytecode/forms/DoubleForm.java | 39 +
.../unpack200/bytecode/forms/FieldRefForm.java | 39 +
.../unpack200/bytecode/forms/FloatRefForm.java | 43 +
.../unpack200/bytecode/forms/IMethodRefForm.java | 56 +
.../harmony/unpack200/bytecode/forms/IincForm.java | 45 +
.../bytecode/forms/InitMethodReferenceForm.java | 56 +
.../unpack200/bytecode/forms/IntRefForm.java | 44 +
.../unpack200/bytecode/forms/LabelForm.java | 80 +
.../unpack200/bytecode/forms/LocalForm.java | 44 +
.../harmony/unpack200/bytecode/forms/LongForm.java | 39 +
.../unpack200/bytecode/forms/LookupSwitchForm.java | 110 ++
.../unpack200/bytecode/forms/MethodRefForm.java | 41 +
.../bytecode/forms/MultiANewArrayForm.java | 51 +
.../bytecode/forms/NarrowClassRefForm.java | 49 +
.../unpack200/bytecode/forms/NewClassRefForm.java | 69 +
.../bytecode/forms/NewInitMethodRefForm.java | 50 +
.../unpack200/bytecode/forms/NoArgumentForm.java | 44 +
.../unpack200/bytecode/forms/ReferenceForm.java | 69 +
.../unpack200/bytecode/forms/ShortForm.java | 42 +
.../bytecode/forms/SingleByteReferenceForm.java | 52 +
.../unpack200/bytecode/forms/StringRefForm.java | 63 +
.../bytecode/forms/SuperFieldRefForm.java | 45 +
.../bytecode/forms/SuperInitMethodRefForm.java | 33 +
.../bytecode/forms/SuperMethodRefForm.java | 45 +
.../unpack200/bytecode/forms/SwitchForm.java | 57 +
.../unpack200/bytecode/forms/TableSwitchForm.java | 113 ++
.../unpack200/bytecode/forms/ThisFieldRefForm.java | 45 +
.../bytecode/forms/ThisInitMethodRefForm.java | 34 +
.../bytecode/forms/ThisMethodRefForm.java | 45 +
.../bytecode/forms/VariableInstructionForm.java | 126 ++
.../harmony/unpack200/bytecode/forms/WideForm.java | 148 ++
142 files changed, 26602 insertions(+), 10 deletions(-)
diff --git a/pom.xml b/pom.xml
index 0a58bb0..0940702 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,6 +61,7 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.
org.tukaani.xz;resolution:=optional,
org.brotli.dec;resolution:=optional,
com.github.luben.zstd;resolution:=optional,
+ org.objectweb.asm;resolution:=optional,
javax.crypto.*;resolution:=optional,
*
</commons.osgi.import>
@@ -87,12 +88,6 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.
<dependencies>
<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.13</version>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>com.github.luben</groupId>
<artifactId>zstd-jni</artifactId>
<version>1.4.5-6</version>
@@ -110,6 +105,22 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.
<version>1.8</version>
<optional>true</optional>
</dependency>
+
+ <!-- Pack200 -->
+ <dependency>
+ <groupId>asm</groupId>
+ <artifactId>asm</artifactId>
+ <version>3.2</version>
+ <optional>true</optional>
+ </dependency>
+
+ <!-- Test -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.13</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
@@ -123,7 +134,7 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.
<scope>test</scope>
</dependency>
- <!-- integration test verifiying OSGi bundle works -->
+ <!-- integration test verifying OSGi bundle works -->
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-native</artifactId>
diff --git a/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorInputStream.java b/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorInputStream.java
index 1d849a3..0336fed 100644
--- a/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorInputStream.java
+++ b/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorInputStream.java
@@ -24,7 +24,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.jar.JarOutputStream;
-import java.util.jar.Pack200;
+import org.apache.commons.compress.java.util.jar.Pack200;
import org.apache.commons.compress.compressors.CompressorInputStream;
import org.apache.commons.compress.utils.CloseShieldFilterInputStream;
diff --git a/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorOutputStream.java b/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorOutputStream.java
index e5be87b..fea31f4 100644
--- a/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorOutputStream.java
+++ b/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorOutputStream.java
@@ -23,7 +23,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import java.util.jar.JarInputStream;
-import java.util.jar.Pack200;
+import org.apache.commons.compress.java.util.jar.Pack200;
import org.apache.commons.compress.compressors.CompressorOutputStream;
diff --git a/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200Utils.java b/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200Utils.java
index df07b86..c57bb1f 100644
--- a/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200Utils.java
+++ b/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200Utils.java
@@ -26,7 +26,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
-import java.util.jar.Pack200;
+import org.apache.commons.compress.java.util.jar.Pack200;
/**
* Utility methods for Pack200.
diff --git a/src/main/java/org/apache/commons/compress/java/util/jar/Pack200.java b/src/main/java/org/apache/commons/compress/java/util/jar/Pack200.java
new file mode 100644
index 0000000..998c7bf
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/java/util/jar/Pack200.java
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.commons.compress.java.util.jar;
+
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.SortedMap;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+
+import org.apache.harmony.archive.internal.nls.Messages;
+
+/**
+ * Class factory for {@link Pack200.Packer} and {@link Pack200.Unpacker}.
+ */
+public abstract class Pack200 {
+
+ private static final String SYSTEM_PROPERTY_PACKER = "java.util.jar.Pack200.Packer"; //$NON-NLS-1$
+
+ private static final String SYSTEM_PROPERTY_UNPACKER = "java.util.jar.Pack200.Unpacker"; //$NON-NLS-1$
+
+ /**
+ * Prevent this class from being instantiated.
+ */
+ private Pack200() {
+ // do nothing
+ }
+
+ /**
+ * Returns a new instance of a packer engine.
+ * <p>
+ * The implementation of the packer engine is defined by the system property
+ * {@code 'java.util.jar.Pack200.Packer'}. If this system property is
+ * defined an instance of the specified class is returned, otherwise the
+ * system's default implementation is returned.
+ *
+ * @return an instance of {@code Packer}
+ */
+ public static Pack200.Packer newPacker() {
+ return (Packer) AccessController
+ .doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ String className = System
+ .getProperty(SYSTEM_PROPERTY_PACKER,
+ "org.apache.harmony.pack200.Pack200PackerAdapter"); //$NON-NLS-1$
+ try {
+ // TODO Not sure if this will cause problems with
+ // loading the packer
+ return ClassLoader.getSystemClassLoader()
+ .loadClass(className).newInstance();
+ } catch (Exception e) {
+ throw new Error(Messages.getString("archive.3E",className), e); //$NON-NLS-1$
+ }
+ }
+ });
+
+ }
+
+ /**
+ * Returns a new instance of a unpacker engine.
+ * <p>
+ * The implementation of the unpacker engine is defined by the system
+ * property {@code 'java.util.jar.Pack200.Unpacker'}. If this system
+ * property is defined an instance of the specified class is returned,
+ * otherwise the system's default implementation is returned.
+ *
+ * @return a instance of {@code Unpacker}.
+ */
+ public static Pack200.Unpacker newUnpacker() {
+ return (Unpacker) AccessController
+ .doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ String className = System
+ .getProperty(SYSTEM_PROPERTY_UNPACKER,
+ "org.apache.harmony.unpack200.Pack200UnpackerAdapter");//$NON-NLS-1$
+ try {
+ return ClassLoader.getSystemClassLoader()
+ .loadClass(className).newInstance();
+ } catch (Exception e) {
+ throw new Error(Messages.getString("archive.3E",className), e); //$NON-NLS-1$
+ }
+ }
+ });
+ }
+
+ /**
+ * The interface defining the API for converting a JAR file to an output
+ * stream in the Pack200 format.
+ */
+ public static interface Packer {
+
+ /**
+ * the format of a class attribute name.
+ */
+ static final String CLASS_ATTRIBUTE_PFX = "pack.class.attribute."; //$NON-NLS-1$
+
+ /**
+ * the format of a code attribute name.
+ */
+ static final String CODE_ATTRIBUTE_PFX = "pack.code.attribute."; //$NON-NLS-1$
+
+ /**
+ * the deflation hint to set in the output archive.
+ */
+ static final String DEFLATE_HINT = "pack.deflate.hint";//$NON-NLS-1$
+
+ /**
+ * the indicated amount of effort to use in compressing the archive.
+ */
+ static final String EFFORT = "pack.effort";//$NON-NLS-1$
+
+ /**
+ * a String representation for {@code error}.
+ */
+ static final String ERROR = "error";//$NON-NLS-1$
+
+ /**
+ * a String representation of {@code false}.
+ */
+ static final String FALSE = "false";//$NON-NLS-1$
+
+ /**
+ * the format of a field attribute name.
+ */
+ static final String FIELD_ATTRIBUTE_PFX = "pack.field.attribute.";//$NON-NLS-1$
+
+ /**
+ * a String representation for {@code keep}.
+ */
+ static final String KEEP = "keep";//$NON-NLS-1$
+
+ /**
+ * decide if all elements shall transmit in their original order.
+ */
+ static final String KEEP_FILE_ORDER = "pack.keep.file.order";//$NON-NLS-1$
+
+ /**
+ * a String representation for {@code latest}.
+ */
+ static final String LATEST = "latest";//$NON-NLS-1$
+
+ /**
+ * the format of a method attribute name.
+ */
+ static final String METHOD_ATTRIBUTE_PFX = "pack.method.attribute.";//$NON-NLS-1$
+
+ /**
+ * if it shall attempt to determine the latest modification time if this
+ * is set to {@code LATEST}.
+ */
+ static final String MODIFICATION_TIME = "pack.modification.time";//$NON-NLS-1$
+
+ /**
+ * a String representation of {@code pass}.
+ */
+ static final String PASS = "pass";//$NON-NLS-1$
+
+ /**
+ * the file that will not be compressed.
+ */
+ static final String PASS_FILE_PFX = "pack.pass.file.";//$NON-NLS-1$
+
+ /**
+ * packer progress as a percentage.
+ */
+ static final String PROGRESS = "pack.progress";//$NON-NLS-1$
+
+ /**
+ * The number of bytes of each archive segment.
+ */
+ static final String SEGMENT_LIMIT = "pack.segment.limit";//$NON-NLS-1$
+
+ /**
+ * a String representation of {@code strip}.
+ */
+ static final String STRIP = "strip";//$NON-NLS-1$
+
+ /**
+ * a String representation of {@code true}.
+ */
+ static final String TRUE = "true";//$NON-NLS-1$
+
+ /**
+ * the action to take if an unknown attribute is encountered.
+ */
+ static final String UNKNOWN_ATTRIBUTE = "pack.unknown.attribute";//$NON-NLS-1$
+
+ /**
+ * Returns a sorted map of the properties of this packer.
+ *
+ * @return the properties of the packer.
+ */
+ SortedMap<String, String> properties();
+
+ /**
+ * Pack the specified JAR file to the specified output stream.
+ *
+ * @param in
+ * JAR file to be compressed.
+ * @param out
+ * stream of compressed data.
+ * @throws IOException
+ * if I/O exception occurs.
+ */
+ void pack(JarFile in, OutputStream out) throws IOException;
+
+ /**
+ * Pack the data from the specified jar input stream to the specified
+ * output stream.
+ *
+ * @param in
+ * stream of uncompressed JAR data.
+ * @param out
+ * stream of compressed data.
+ * @throws IOException
+ * if I/O exception occurs.
+ */
+ void pack(JarInputStream in, OutputStream out) throws IOException;
+
+ /**
+ * add a listener for PropertyChange events
+ *
+ * @param listener
+ * the listener to listen if PropertyChange events occurs
+ */
+ void addPropertyChangeListener(PropertyChangeListener listener);
+
+ /**
+ * remove a listener
+ *
+ * @param listener
+ * listener to remove
+ */
+ void removePropertyChangeListener(PropertyChangeListener listener);
+ }
+
+ /**
+ * The interface defining the API for converting a packed stream in the
+ * Pack200 format to a JAR file.
+ */
+ public static interface Unpacker {
+
+ /**
+ * The String indicating if the unpacker should ignore all transmitted
+ * values,can be replaced by either {@code true} or {@code false}.
+ */
+ static final String DEFLATE_HINT = "unpack.deflate.hint";//$NON-NLS-1$
+
+ /**
+ * a String representation of {@code false}.
+ */
+ static final String FALSE = "false";//$NON-NLS-1$
+
+ /**
+ * a String representation of {@code keep}.
+ */
+ static final String KEEP = "keep";//$NON-NLS-1$
+
+ /**
+ * the progress as a {@code percentage}.
+ */
+ static final String PROGRESS = "unpack.progress";//$NON-NLS-1$
+
+ /**
+ * a String representation of {@code true}.
+ */
+ static final String TRUE = "true";//$NON-NLS-1$
+
+ /**
+ * Returns a sorted map of the properties of this unpacker.
+ *
+ * @return the properties of unpacker.
+ */
+ SortedMap<String, String> properties();
+
+ /**
+ * Unpack the specified stream to the specified JAR output stream.
+ *
+ * @param in
+ * stream to uncompressed.
+ * @param out
+ * JAR output stream of uncompressed data.
+ * @throws IOException
+ * if I/O exception occurs.
+ */
+ void unpack(InputStream in, JarOutputStream out) throws IOException;
+
+ /**
+ * Unpack the contents of the specified {@code File} to the specified
+ * JAR output stream.
+ *
+ * @param in
+ * file to be uncompressed.
+ * @param out
+ * JAR output stream of uncompressed data.
+ * @throws IOException
+ * if I/O exception occurs.
+ */
+ void unpack(File in, JarOutputStream out) throws IOException;
+
+ /**
+ * add a listener for {@code PropertyChange} events.
+ *
+ * @param listener
+ * the listener to listen if {@code PropertyChange} events
+ * occurs.
+ */
+ void addPropertyChangeListener(PropertyChangeListener listener);
+
+ /**
+ * remove a listener.
+ *
+ * @param listener
+ * listener to remove.
+ */
+ void removePropertyChangeListener(PropertyChangeListener listener);
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java b/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java
new file mode 100644
index 0000000..e80a150
--- /dev/null
+++ b/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+/*
+ * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL.
+ * All changes made to this file manually will be overwritten
+ * if this tool runs again. Better make changes in the template file.
+ */
+
+package org.apache.harmony.archive.internal.nls;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+//import org.apache.harmony.kernel.vm.VM;
+
+/**
+ * This class retrieves strings from a resource bundle and returns them,
+ * formatting them with MessageFormat when required.
+ * <p>
+ * It is used by the system classes to provide national language support, by
+ * looking up messages in the <code>
+ * org.apache.harmony.archive.internal.nls.messages
+ * </code>
+ * resource bundle. Note that if this file is not available, or an invalid key
+ * is looked up, or resource bundle support is not available, the key itself
+ * will be returned as the associated message. This means that the <em>KEY</em>
+ * should a reasonable human-readable (english) string.
+ *
+ */
+public class Messages {
+
+ // ResourceBundle holding the system messages.
+ static private ResourceBundle bundle = null;
+
+ /**
+ * Retrieves a message which has no arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg) {
+ if (bundle == null) {
+ return msg;
+ }
+ try {
+ return bundle.getString(msg);
+ } catch (MissingResourceException e) {
+ return "Missing message: " + msg; //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Retrieves a message which takes 1 argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * Object the object to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object arg) {
+ return getString(msg, new Object[] { arg });
+ }
+
+ /**
+ * Retrieves a message which takes 1 integer argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * int the integer to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, int arg) {
+ return getString(msg, new Object[] { Integer.toString(arg) });
+ }
+
+ /**
+ * Retrieves a message which takes 1 character argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * char the character to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, char arg) {
+ return getString(msg, new Object[] { String.valueOf(arg) });
+ }
+
+ /**
+ * Retrieves a message which takes 2 arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg1
+ * Object an object to insert in the formatted output.
+ * @param arg2
+ * Object another object to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object arg1, Object arg2) {
+ return getString(msg, new Object[] { arg1, arg2 });
+ }
+
+ /**
+ * Retrieves a message which takes several arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param args
+ * Object[] the objects to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object[] args) {
+ String format = msg;
+
+ if (bundle != null) {
+ try {
+ format = bundle.getString(msg);
+ } catch (MissingResourceException e) {
+ // ignore
+ }
+ }
+
+ return format(format, args);
+ }
+
+ /**
+ * Generates a formatted text string given a source string containing
+ * "argument markers" of the form "{argNum}" where each argNum must be in
+ * the range 0..9. The result is generated by inserting the toString of each
+ * argument into the position indicated in the string.
+ * <p>
+ * To insert the "{" character into the output, use a single backslash
+ * character to escape it (i.e. "\{"). The "}" character does not need to be
+ * escaped.
+ *
+ * @param format
+ * String the format to use when printing.
+ * @param args
+ * Object[] the arguments to use.
+ * @return String the formatted message.
+ */
+ public static String format(String format, Object[] args) {
+ StringBuilder answer = new StringBuilder(format.length()
+ + (args.length * 20));
+ String[] argStrings = new String[args.length];
+ for (int i = 0; i < args.length; ++i) {
+ if (args[i] == null) {
+ argStrings[i] = "<null>"; //$NON-NLS-1$
+ } else {
+ argStrings[i] = args[i].toString();
+ }
+ }
+ int lastI = 0;
+ for (int i = format.indexOf('{', 0); i >= 0; i = format.indexOf('{',
+ lastI)) {
+ if (i != 0 && format.charAt(i - 1) == '\\') {
+ // It's escaped, just print and loop.
+ if (i != 1) {
+ answer.append(format.substring(lastI, i - 1));
+ }
+ answer.append('{');
+ lastI = i + 1;
+ } else {
+ // It's a format character.
+ if (i > format.length() - 3) {
+ // Bad format, just print and loop.
+ answer.append(format.substring(lastI, format.length()));
+ lastI = format.length();
+ } else {
+ int argnum = (byte) Character.digit(format.charAt(i + 1),
+ 10);
+ if (argnum < 0 || format.charAt(i + 2) != '}') {
+ // Bad format, just print and loop.
+ answer.append(format.substring(lastI, i + 1));
+ lastI = i + 1;
+ } else {
+ // Got a good one!
+ answer.append(format.substring(lastI, i));
+ if (argnum >= argStrings.length) {
+ answer.append("<missing argument>"); //$NON-NLS-1$
+ } else {
+ answer.append(argStrings[argnum]);
+ }
+ lastI = i + 3;
+ }
+ }
+ }
+ }
+ if (lastI < format.length()) {
+ answer.append(format.substring(lastI, format.length()));
+ }
+ return answer.toString();
+ }
+
+ /**
+ * Changes the locale of the messages.
+ *
+ * @param locale
+ * Locale the locale to change to.
+ */
+ static public ResourceBundle setLocale(final Locale locale,
+ final String resource) {
+ try {
+ // VM.bootCallerClassLoader() returns null
+ final ClassLoader loader = null;//VM.bootCallerClassLoader();
+ return (ResourceBundle) AccessController
+ .doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ return ResourceBundle.getBundle(resource, locale,
+ loader != null ? loader : ClassLoader
+ .getSystemClassLoader());
+ }
+ });
+ } catch (MissingResourceException e) {
+ // ignore
+ }
+ return null;
+ }
+
+ static {
+ // Attempt to load the messages.
+ try {
+ bundle = setLocale(Locale.getDefault(),
+ "org.apache.harmony.archive.internal.nls.messages"); //$NON-NLS-1$
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/Archive.java b/src/main/java/org/apache/harmony/pack200/Archive.java
new file mode 100644
index 0000000..92c0ca4
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/Archive.java
@@ -0,0 +1,332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * Archive is the main entry point to pack200 and represents a packed archive.
+ * An archive is constructed with either a JarInputStream and an output stream
+ * or a JarFile as input and an OutputStream. Options can be set, then
+ * <code>pack()</code> is called, to pack the Jar file into a pack200 archive.
+ */
+public class Archive {
+
+ private final JarInputStream jarInputStream;
+ private final OutputStream outputStream;
+ private JarFile jarFile;
+ private long currentSegmentSize;
+ private final PackingOptions options;
+
+ /**
+ * Creates an Archive with streams for the input and output.
+ *
+ * @param inputStream
+ * @param outputStream
+ * @param options - packing options (if null then defaults are used)
+ * @throws IOException
+ */
+ public Archive(JarInputStream inputStream, OutputStream outputStream,
+ PackingOptions options) throws IOException {
+ jarInputStream = inputStream;
+ if(options == null) {
+ // use all defaults
+ options = new PackingOptions();
+ }
+ this.options = options;
+ if (options.isGzip()) {
+ outputStream = new GZIPOutputStream(outputStream);
+ }
+ this.outputStream = new BufferedOutputStream(outputStream);
+ PackingUtils.config(options);
+ }
+
+ /**
+ * Creates an Archive with the given input file and a stream for the output
+ *
+ * @param jarFile - the input file
+ * @param outputStream
+ * @param options - packing options (if null then defaults are used)
+ * @throws IOException
+ */
+ public Archive(JarFile jarFile, OutputStream outputStream,
+ PackingOptions options) throws IOException {
+ if(options == null) { // use all defaults
+ options = new PackingOptions();
+ }
+ this.options = options;
+ if (options.isGzip()) {
+ outputStream = new GZIPOutputStream(outputStream);
+ }
+ this.outputStream = new BufferedOutputStream(outputStream);
+ this.jarFile = jarFile;
+ jarInputStream = null;
+ PackingUtils.config(options);
+ }
+
+ /**
+ * Pack the archive
+ * @throws Pack200Exception
+ * @throws IOException
+ */
+ public void pack() throws Pack200Exception, IOException {
+ if (0 == options.getEffort()) {
+ doZeroEffortPack();
+ } else {
+ doNormalPack();
+ }
+ }
+
+ private void doZeroEffortPack() throws IOException, Pack200Exception {
+ PackingUtils.log("Start to perform a zero-effort packing");
+ if (jarInputStream != null) {
+ PackingUtils.copyThroughJar(jarInputStream, outputStream);
+ } else {
+ PackingUtils.copyThroughJar(jarFile, outputStream);
+ }
+ }
+
+ private void doNormalPack() throws IOException, Pack200Exception {
+ PackingUtils.log("Start to perform a normal packing");
+ List packingFileList;
+ if (jarInputStream != null) {
+ packingFileList = PackingUtils.getPackingFileListFromJar(
+ jarInputStream, options.isKeepFileOrder());
+ } else {
+ packingFileList = PackingUtils.getPackingFileListFromJar(jarFile,
+ options.isKeepFileOrder());
+ }
+
+ List segmentUnitList = splitIntoSegments(packingFileList);
+ int previousByteAmount = 0;
+ int packedByteAmount = 0;
+
+ int segmentSize = segmentUnitList.size();
+ SegmentUnit segmentUnit;
+ for (int index = 0; index < segmentSize; index++) {
+ segmentUnit = (SegmentUnit) segmentUnitList.get(index);
+ new Segment().pack(segmentUnit, outputStream, options);
+ previousByteAmount += segmentUnit.getByteAmount();
+ packedByteAmount += segmentUnit.getPackedByteAmount();
+ }
+
+ PackingUtils.log("Total: Packed " + previousByteAmount
+ + " input bytes of " + packingFileList.size() + " files into "
+ + packedByteAmount + " bytes in " + segmentSize + " segments");
+
+ outputStream.close();
+ }
+
+ private List splitIntoSegments(List packingFileList) throws IOException,
+ Pack200Exception {
+ List segmentUnitList = new ArrayList();
+ List classes = new ArrayList();
+ List files = new ArrayList();
+ long segmentLimit = options.getSegmentLimit();
+
+ int size = packingFileList.size();
+ PackingFile packingFile;
+ for (int index = 0; index < size; index++) {
+ packingFile = (PackingFile) packingFileList.get(index);
+ if (!addJarEntry(packingFile, classes, files)) {
+ // not added because segment has reached maximum size
+ segmentUnitList.add(new SegmentUnit(classes, files));
+ classes = new ArrayList();
+ files = new ArrayList();
+ currentSegmentSize = 0;
+ // add the jar to a new segment
+ addJarEntry(packingFile, classes, files);
+ // ignore the size of first entry for compatibility with RI
+ currentSegmentSize = 0;
+ } else if (segmentLimit == 0 && estimateSize(packingFile) > 0) {
+ // create a new segment for each class unless size is 0
+ segmentUnitList.add(new SegmentUnit(classes, files));
+ classes = new ArrayList();
+ files = new ArrayList();
+ }
+ }
+ if (classes.size() > 0 && files.size() > 0) {
+ segmentUnitList.add(new SegmentUnit(classes, files));
+ }
+ return segmentUnitList;
+ }
+
+ private boolean addJarEntry(PackingFile packingFile, List javaClasses,
+ List files) throws IOException, Pack200Exception {
+ long segmentLimit = options.getSegmentLimit();
+ if (segmentLimit != -1 && segmentLimit != 0) {
+ // -1 is a special case where only one segment is created and
+ // 0 is a special case where one segment is created for each file
+ // except for files in "META-INF"
+ long packedSize = estimateSize(packingFile);
+ if (packedSize + currentSegmentSize > segmentLimit
+ && currentSegmentSize > 0) {
+ // don't add this JarEntry to the current segment
+ return false;
+ } else {
+ // do add this JarEntry
+ currentSegmentSize += packedSize;
+ }
+ }
+
+ String name = packingFile.getName();
+ if (name.endsWith(".class") && !options.isPassFile(name)) {
+ Pack200ClassReader classParser = new Pack200ClassReader(
+ packingFile.contents);
+ classParser.setFileName(name);
+ javaClasses.add(classParser);
+ packingFile.contents = new byte[0];
+ }
+ files.add(packingFile);
+ return true;
+ }
+
+ private long estimateSize(PackingFile packingFile) {
+ // The heuristic used here is for compatibility with the RI and should
+ // not be changed
+ String name = packingFile.getName();
+ if (name.startsWith("META-INF") || name.startsWith("/META-INF")) {
+ return 0;
+ } else {
+ long fileSize = packingFile.contents.length;
+ if (fileSize < 0) {
+ fileSize = 0;
+ }
+ return name.length() + fileSize + 5;
+ }
+ }
+
+ static class SegmentUnit {
+
+ private final List classList;
+
+ private final List fileList;
+
+ private int byteAmount = 0;
+
+ private int packedByteAmount = 0;
+
+ public SegmentUnit(List classes, List files) {
+ classList = classes;
+ fileList = files;
+
+ // Calculate the amount of bytes in classes and files before packing
+ Pack200ClassReader classReader;
+ for (Iterator iterator = classList.iterator(); iterator.hasNext();) {
+ classReader = (Pack200ClassReader) iterator.next();
+ byteAmount += classReader.b.length;
+ }
+
+ PackingFile file;
+ for (Iterator iterator = fileList.iterator(); iterator.hasNext();) {
+ file = (PackingFile) iterator.next();
+ byteAmount += file.contents.length;
+ }
+ }
+
+ public List getClassList() {
+ return classList;
+ }
+
+ public int classListSize() {
+ return classList.size();
+ }
+
+ public int fileListSize() {
+ return fileList.size();
+ }
+
+ public List getFileList() {
+ return fileList;
+ }
+
+ public int getByteAmount() {
+ return byteAmount;
+ }
+
+ public int getPackedByteAmount() {
+ return packedByteAmount;
+ }
+
+ public void addPackedByteAmount(int amount) {
+ packedByteAmount += amount;
+ }
+ }
+
+ static class PackingFile {
+
+ private final String name;
+ private byte[] contents;
+ private final long modtime;
+ private final boolean deflateHint;
+ private final boolean isDirectory;
+
+ public PackingFile(String name, byte[] contents, long modtime) {
+ this.name = name;
+ this.contents = contents;
+ this.modtime = modtime;
+ deflateHint = false;
+ isDirectory = false;
+ }
+
+ public PackingFile(byte[] bytes, JarEntry jarEntry) {
+ name = jarEntry.getName();
+ contents = bytes;
+ modtime = jarEntry.getTime();
+ deflateHint = jarEntry.getMethod() == JarEntry.DEFLATED;
+ isDirectory = jarEntry.isDirectory();
+ }
+
+ public byte[] getContents() {
+ return contents;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public long getModtime() {
+ return modtime;
+ }
+
+ public void setContents(byte[] contents) {
+ this.contents = contents;
+ }
+
+ public boolean isDefalteHint() {
+ return deflateHint;
+ }
+
+ public boolean isDirectory(){
+ return isDirectory;
+ }
+
+ public String toString() {
+ return name;
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/AttributeDefinitionBands.java b/src/main/java/org/apache/harmony/pack200/AttributeDefinitionBands.java
new file mode 100644
index 0000000..5afb5ba
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/AttributeDefinitionBands.java
@@ -0,0 +1,260 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.objectweb.asm.Attribute;
+
+/**
+ * Attribute Definition bands define how any unknown attributes should be read
+ * by the decompressor.
+ */
+public class AttributeDefinitionBands extends BandSet {
+
+ public static final int CONTEXT_CLASS = 0;
+ public static final int CONTEXT_CODE = 3;
+ public static final int CONTEXT_FIELD = 1;
+ public static final int CONTEXT_METHOD = 2;
+
+ private final List classAttributeLayouts = new ArrayList();
+ private final List methodAttributeLayouts = new ArrayList();
+ private final List fieldAttributeLayouts = new ArrayList();
+ private final List codeAttributeLayouts = new ArrayList();
+
+ private final List attributeDefinitions = new ArrayList();
+
+ private final CpBands cpBands;
+ private final Segment segment;
+
+ public AttributeDefinitionBands(Segment segment, int effort,
+ Attribute[] attributePrototypes) {
+ super(effort, segment.getSegmentHeader());
+ this.cpBands = segment.getCpBands();
+ this.segment = segment;
+ Map classLayouts = new HashMap();
+ Map methodLayouts = new HashMap();
+ Map fieldLayouts = new HashMap();
+ Map codeLayouts = new HashMap();
+
+ for (int i = 0; i < attributePrototypes.length; i++) {
+ NewAttribute newAttribute = (NewAttribute) attributePrototypes[i];
+ if(!(newAttribute instanceof NewAttribute.ErrorAttribute) && !(newAttribute instanceof NewAttribute.PassAttribute) && !(newAttribute instanceof NewAttribute.StripAttribute)) {
+ if (newAttribute.isContextClass()) {
+ classLayouts.put(newAttribute.type, newAttribute.getLayout());
+ }
+ if (newAttribute.isContextMethod()) {
+ methodLayouts.put(newAttribute.type, newAttribute.getLayout());
+ }
+ if (newAttribute.isContextField()) {
+ fieldLayouts.put(newAttribute.type, newAttribute.getLayout());
+ }
+ if (newAttribute.isContextCode()) {
+ codeLayouts.put(newAttribute.type, newAttribute.getLayout());
+ }
+ }
+ }
+ if (classLayouts.keySet().size() > 7) {
+ segmentHeader.setHave_class_flags_hi(true);
+ }
+ if (methodLayouts.keySet().size() > 6) {
+ segmentHeader.setHave_method_flags_hi(true);
+ }
+ if (fieldLayouts.keySet().size() > 10) {
+ segmentHeader.setHave_field_flags_hi(true);
+ }
+ if (codeLayouts.keySet().size() > 15) {
+ segmentHeader.setHave_code_flags_hi(true);
+ }
+ int[] availableClassIndices = new int[] { 25, 26, 27, 28, 29, 30, 31 };
+ if (classLayouts.size() > 7) {
+ availableClassIndices = addHighIndices(availableClassIndices);
+ }
+ addAttributeDefinitions(classLayouts, availableClassIndices,
+ CONTEXT_CLASS);
+ int[] availableMethodIndices = new int[] { 26, 27, 28, 29, 30, 31 };
+ if (methodAttributeLayouts.size() > 6) {
+ availableMethodIndices = addHighIndices(availableMethodIndices);
+ }
+ addAttributeDefinitions(methodLayouts, availableMethodIndices,
+ CONTEXT_METHOD);
+ int[] availableFieldIndices = new int[] { 18, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31 };
+ if (fieldAttributeLayouts.size() > 10) {
+ availableFieldIndices = addHighIndices(availableFieldIndices);
+ }
+ addAttributeDefinitions(fieldLayouts, availableFieldIndices,
+ CONTEXT_FIELD);
+ int[] availableCodeIndices = new int[] { 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31 };
+ if (codeAttributeLayouts.size() > 15) {
+ availableCodeIndices = addHighIndices(availableCodeIndices);
+ }
+ addAttributeDefinitions(codeLayouts, availableCodeIndices, CONTEXT_CODE);
+ }
+
+ /**
+ * All input classes for the segment have now been read in, so this method
+ * is called so that this class can calculate/complete anything it could not
+ * do while classes were being read.
+ */
+ public void finaliseBands() {
+ addSyntheticDefinitions();
+ segmentHeader.setAttribute_definition_count(attributeDefinitions.size());
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ PackingUtils.log("Writing attribute definition bands...");
+ int[] attributeDefinitionHeader = new int[attributeDefinitions.size()];
+ int[] attributeDefinitionName = new int[attributeDefinitions.size()];
+ int[] attributeDefinitionLayout = new int[attributeDefinitions.size()];
+ for (int i = 0; i < attributeDefinitionLayout.length; i++) {
+ AttributeDefinition def = (AttributeDefinition) attributeDefinitions
+ .get(i);
+ attributeDefinitionHeader[i] = def.contextType
+ | (def.index + 1 << 2);
+ attributeDefinitionName[i] = def.name.getIndex();
+ attributeDefinitionLayout[i] = def.layout.getIndex();
+ }
+
+ byte[] encodedBand = encodeBandInt("attributeDefinitionHeader",
+ attributeDefinitionHeader, Codec.BYTE1);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from attributeDefinitionHeader["
+ + attributeDefinitionHeader.length + "]");
+
+ encodedBand = encodeBandInt("attributeDefinitionName",
+ attributeDefinitionName, Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from attributeDefinitionName["
+ + attributeDefinitionName.length + "]");
+
+ encodedBand = encodeBandInt("attributeDefinitionLayout",
+ attributeDefinitionLayout, Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from attributeDefinitionLayout["
+ + attributeDefinitionLayout.length + "]");
+ }
+
+ private void addSyntheticDefinitions() {
+ boolean anySytheticClasses = segment.getClassBands()
+ .isAnySyntheticClasses();
+ boolean anySyntheticMethods = segment.getClassBands()
+ .isAnySyntheticMethods();
+ boolean anySyntheticFields = segment.getClassBands()
+ .isAnySyntheticFields();
+ if (anySytheticClasses || anySyntheticMethods || anySyntheticFields) {
+ CPUTF8 syntheticUTF = cpBands.getCPUtf8("Synthetic");
+ CPUTF8 emptyUTF = cpBands.getCPUtf8("");
+ if (anySytheticClasses) {
+ attributeDefinitions.add(new AttributeDefinition(12,
+ CONTEXT_CLASS, syntheticUTF, emptyUTF));
+ }
+ if (anySyntheticMethods) {
+ attributeDefinitions.add(new AttributeDefinition(12,
+ CONTEXT_METHOD, syntheticUTF, emptyUTF));
+ }
+ if (anySyntheticFields) {
+ attributeDefinitions.add(new AttributeDefinition(12,
+ CONTEXT_FIELD, syntheticUTF, emptyUTF));
+ }
+ }
+ }
+
+ private int[] addHighIndices(int[] availableIndices) {
+ int[] temp = new int[availableIndices.length + 32];
+ for (int i = 0; i < availableIndices.length; i++) {
+ temp[i] = availableIndices[i];
+ }
+ int j = 32;
+ for (int i = availableIndices.length; i < temp.length; i++) {
+ temp[i] = j;
+ j++;
+ }
+ return temp;
+ }
+
+ private void addAttributeDefinitions(Map layouts, int[] availableIndices,
+ int contextType) {
+ int i = 0;
+ for (Iterator iterator = layouts.keySet().iterator(); iterator
+ .hasNext();) {
+ String name = (String) iterator.next();
+ String layout = (String) layouts.get(name);
+ int index = availableIndices[i];
+ AttributeDefinition definition = new AttributeDefinition(index,
+ contextType, cpBands.getCPUtf8(name), cpBands
+ .getCPUtf8(layout));
+ attributeDefinitions.add(definition);
+ switch (contextType) {
+ case CONTEXT_CLASS:
+ classAttributeLayouts.add(definition);
+ break;
+ case CONTEXT_METHOD:
+ methodAttributeLayouts.add(definition);
+ break;
+ case CONTEXT_FIELD:
+ fieldAttributeLayouts.add(definition);
+ break;
+ case CONTEXT_CODE:
+ codeAttributeLayouts.add(definition);
+ }
+ }
+ }
+
+ public List getClassAttributeLayouts() {
+ return classAttributeLayouts;
+ }
+
+ public List getMethodAttributeLayouts() {
+ return methodAttributeLayouts;
+ }
+
+ public List getFieldAttributeLayouts() {
+ return fieldAttributeLayouts;
+ }
+
+ public List getCodeAttributeLayouts() {
+ return codeAttributeLayouts;
+ }
+
+ public static class AttributeDefinition {
+
+ public int index;
+ public int contextType;
+ public CPUTF8 name;
+ public CPUTF8 layout;
+
+ public AttributeDefinition(int index, int contextType, CPUTF8 name,
+ CPUTF8 layout) {
+ this.index = index;
+ this.contextType = contextType;
+ this.name = name;
+ this.layout = layout;
+ }
+
+ }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/BHSDCodec.java b/src/main/java/org/apache/harmony/pack200/BHSDCodec.java
new file mode 100644
index 0000000..8836098
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/BHSDCodec.java
@@ -0,0 +1,516 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A BHSD codec is a means of encoding integer values as a sequence of bytes or
+ * vice versa using a specified "BHSD" encoding mechanism. It uses a
+ * variable-length encoding and a modified sign representation such that small
+ * numbers are represented as a single byte, whilst larger numbers take more
+ * bytes to encode. The number may be signed or unsigned; if it is unsigned, it
+ * can be weighted towards positive numbers or equally distributed using a one's
+ * complement. The Codec also supports delta coding, where a sequence of numbers
+ * is represented as a series of first-order differences. So a delta encoding of
+ * the integers [1..10] would be represented as a sequence of 10x1s. This allows
+ * the absolute value of a coded integer to fall outside of the 'small number'
+ * range, whilst still being encoded as a single byte.
+ *
+ * A BHSD codec is configured with four parameters:
+ * <dl>
+ * <dt>B</dt>
+ * <dd>The maximum number of bytes that each value is encoded as. B must be a
+ * value between [1..5]. For a pass-through coding (where each byte is encoded
+ * as itself, aka {@link #BYTE1}, B is 1 (each byte takes a maximum of 1 byte).</dd>
+ * <dt>H</dt>
+ * <dd>The radix of the integer. Values are defined as a sequence of values,
+ * where value <code>n</code> is multiplied by <code>H^<sup>n</sup></code>.
+ * So the number 1234 may be represented as the sequence 4 3 2 1 with a radix
+ * (H) of 10. Note that other permutations are also possible; 43 2 1 will also
+ * encode 1234. The co-parameter L is defined as 256-H. This is important
+ * because only the last value in a sequence may be < L; all prior values
+ * must be > L.</dd>
+ * <dt>S</dt>
+ * <dd>Whether the codec represents signed values (or not). This may have 3
+ * values; 0 (unsigned), 1 (signed, one's complement) or 2 (signed, two's
+ * complement)</dd>
+ * <dt>D</dt>
+ * <dd>Whether the codec represents a delta encoding. This may be 0 (no delta)
+ * or 1 (delta encoding). A delta encoding of 1 indicates that values are
+ * cumulative; a sequence of <code>1 1 1 1 1</code> will represent the
+ * sequence <code>1 2 3 4 5</code>. For this reason, the codec supports two
+ * variants of decode; one {@link #decode(InputStream, long) with} and one
+ * {@link #decode(InputStream) without} a <code>last</code> parameter. If the
+ * codec is a non-delta encoding, then the value is ignored if passed. If the
+ * codec is a delta encoding, it is a run-time error to call the value without
+ * the extra parameter, and the previous value should be returned. (It was
+ * designed this way to support multi-threaded access without requiring a new
+ * instance of the Codec to be cloned for each use.)
+ * <dt>
+ * </dl>
+ *
+ * Codecs are notated as (B,H,S,D) and either D or S,D may be omitted if zero.
+ * Thus {@link #BYTE1} is denoted (1,256,0,0) or (1,256). The
+ * {@link #toString()} method prints out the condensed form of the encoding.
+ * Often, the last character in the name ({@link #BYTE1}, {@link #UNSIGNED5})
+ * gives a clue as to the B value. Those that start with U ({@link #UDELTA5},
+ * {@link #UNSIGNED5}) are unsigned; otherwise, in most cases, they are signed.
+ * The presence of the word Delta ({@link #DELTA5}, {@link #UDELTA5})
+ * indicates a delta encoding is used.
+ *
+ */
+public final class BHSDCodec extends Codec {
+
+ /**
+ * The maximum number of bytes in each coding word
+ */
+ private final int b;
+
+ /**
+ * Whether delta encoding is used (0=false,1=true)
+ */
+ private final int d;
+
+ /**
+ * The radix of the encoding
+ */
+ private final int h;
+
+ /**
+ * The co-parameter of h; 256-h
+ */
+ private final int l;
+
+ /**
+ * Represents signed numbers or not (0=unsigned,1/2=signed)
+ */
+ private final int s;
+
+ private long cardinality;
+
+ private final long smallest;
+
+ private final long largest;
+
+ /**
+ * radix^i powers
+ */
+ private final long[] powers;
+
+ /**
+ * Constructs an unsigned, non-delta Codec with the given B and H values.
+ *
+ * @param b
+ * the maximum number of bytes that a value can be encoded as
+ * [1..5]
+ * @param h
+ * the radix of the encoding [1..256]
+ */
+ public BHSDCodec(int b, int h) {
+ this(b, h, 0, 0);
+ }
+
+ /**
+ * Constructs a non-delta Codec with the given B, H and S values.
+ *
+ * @param b
+ * the maximum number of bytes that a value can be encoded as
+ * [1..5]
+ * @param h
+ * the radix of the encoding [1..256]
+ * @param s
+ * whether the encoding represents signed numbers (s=0 is
+ * unsigned; s=1 is signed with 1s complement; s=2 is signed with ?)
+ */
+ public BHSDCodec(int b, int h, int s) {
+ this(b, h, s, 0);
+ }
+
+ /**
+ * Constructs a Codec with the given B, H, S and D values.
+ *
+ * @param b
+ * the maximum number of bytes that a value can be encoded as
+ * [1..5]
+ * @param h
+ * the radix of the encoding [1..256]
+ * @param s
+ * whether the encoding represents signed numbers (s=0 is
+ * unsigned; s=1 is signed with 1s complement; s=2 is signed with ?)
+ * @param d
+ * whether this is a delta encoding (d=0 is non-delta; d=1 is
+ * delta)
+ */
+ public BHSDCodec(int b, int h, int s, int d) {
+ if (b < 1 || b > 5) {
+ throw new IllegalArgumentException("1<=b<=5");
+ }
+ if (h < 1 || h > 256) {
+ throw new IllegalArgumentException("1<=h<=256");
+ }
+ if (s < 0 || s > 2) {
+ throw new IllegalArgumentException("0<=s<=2");
+ }
+ if (d < 0 || d > 1) {
+ throw new IllegalArgumentException("0<=d<=1");
+ }
+ if (b == 1 && h != 256) {
+ throw new IllegalArgumentException("b=1 -> h=256");
+ }
+ if (h == 256 && b == 5) {
+ throw new IllegalArgumentException("h=256 -> b!=5");
+ }
+ this.b = b;
+ this.h = h;
+ this.s = s;
+ this.d = d;
+ this.l = 256 - h;
+ if (h == 1) {
+ cardinality = b * 255 + 1;
+ } else {
+ cardinality = (long) ((long) (l * (1 - Math.pow(h, b)) / (1 - h)) + Math
+ .pow(h, b));
+ }
+ smallest = calculateSmallest();
+ largest = calculateLargest();
+
+ powers = new long[b];
+ for(int c = 0; c < b; c++) {
+ powers[c] = (long)Math.pow(h, c);
+ }
+ }
+
+ /**
+ * Returns the cardinality of this codec; that is, the number of distinct
+ * values that it can contain.
+ *
+ * @return the cardinality of this codec
+ */
+ public long cardinality() {
+ return cardinality;
+ }
+
+ public int decode(InputStream in) throws IOException, Pack200Exception {
+ if (d != 0) {
+ throw new Pack200Exception(
+ "Delta encoding used without passing in last value; this is a coding error");
+ }
+ return decode(in, 0);
+ }
+
+ public int decode(InputStream in, long last) throws IOException,
+ Pack200Exception {
+ int n = 0;
+ long z = 0;
+ long x = 0;
+
+ do {
+ x = in.read();
+ lastBandLength ++;
+ z += x * powers[n];
+ n++;
+ } while (x >= l && n < b);
+
+ if (x == -1) {
+ throw new EOFException("End of stream reached whilst decoding");
+ }
+
+ if (isSigned()) {
+ int u = ((1 << s) - 1);
+ if ((z & u) == u) {
+ z = z >>> s ^ -1L;
+ } else {
+ z = z - (z >>> s);
+ }
+ }
+ // This algorithm does the same thing, but is probably slower. Leaving
+ // in for now for readability
+ // if(isSigned()) {
+ // long u = z;
+ // long twoPowS = (long)Math.pow(2, s);
+ // double twoPowSMinusOne = twoPowS-1;
+ // if(u % twoPowS < twoPowSMinusOne) {
+ // if(cardinality < Math.pow(2, 32)) {
+ // z = (long) (u - (Math.floor(u/ twoPowS)));
+ // } else {
+ // z = cast32((long) (u - (Math.floor(u/ twoPowS))));
+ // }
+ // } else {
+ // z = (long) (-Math.floor(u/ twoPowS) - 1);
+ // }
+ // }
+ if (isDelta()) {
+ z += last;
+ }
+ return (int)z;
+ }
+
+ public int[] decodeInts(int n, InputStream in) throws IOException,
+ Pack200Exception {
+ int[] band = super.decodeInts(n, in);
+ if (isDelta()) {
+ for (int i = 0; i < band.length; i++) {
+ while (band[i] > largest) {
+ band[i] -= cardinality;
+ }
+ while (band[i] < smallest) {
+ band[i] += cardinality;
+ }
+ }
+ }
+ return band;
+ }
+
+ public int[] decodeInts(int n, InputStream in, int firstValue)
+ throws IOException, Pack200Exception {
+ int[] band = super.decodeInts(n, in, firstValue);
+ if (isDelta()) {
+ for (int i = 0; i < band.length; i++) {
+ while (band[i] > largest) {
+ band[i] -= cardinality;
+ }
+ while (band[i] < smallest) {
+ band[i] += cardinality;
+ }
+ }
+ }
+ return band;
+ }
+
+ // private long cast32(long u) {
+ // u = (long) ((long) ((u + Math.pow(2, 31)) % Math.pow(2, 32)) -
+ // Math.pow(2, 31));
+ // return u;
+ // }
+
+ /**
+ * True if this encoding can code the given value
+ *
+ * @param value
+ * the value to check
+ * @return <code>true</code> if the encoding can encode this value
+ */
+ public boolean encodes(long value) {
+ return value >= smallest && value <= largest;
+ }
+
+ public byte[] encode(int value, int last) throws Pack200Exception {
+ if(!encodes(value)) {
+ throw new Pack200Exception("The codec " + toString()
+ + " does not encode the value " + value);
+ }
+
+ long z = value;
+ if (isDelta()) {
+ z -= last;
+ }
+ if (isSigned()) {
+ if(z < Integer.MIN_VALUE) {
+ z += 4294967296L;
+ } else if (z > Integer.MAX_VALUE) {
+ z -= 4294967296L;
+ }
+ if (z < 0) {
+ z = (-z << s) - 1;
+ } else {
+ if (s == 1) {
+ z = z << s;
+ } else {
+ z += (z - z % 3) / 3;
+ }
+ }
+ } else {
+ if (z < 0) {
+ // Need to use integer overflow here to represent negatives.
+ if (cardinality < 4294967296L) {
+ z += cardinality;
+ } else {
+ z += 4294967296L; // this value is equal to (1 << 32).
+ }
+ }
+ }
+ if (z < 0) {
+ throw new Pack200Exception("unable to encode");
+ }
+
+ List byteList = new ArrayList();
+ for (int n = 0; n < b; n++) {
+ long byteN;
+ if (z < l) {
+ byteN = z;
+ } else {
+ byteN = z % h;
+ while (byteN < l) {
+ byteN += h;
+ }
+ }
+ byteList.add(new Byte((byte) byteN));
+ if (byteN < l) {
+ break;
+ }
+ z -= byteN;
+ z /= h;
+ }
+ byte[] bytes = new byte[byteList.size()];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = ((Byte) byteList.get(i)).byteValue();
+ }
+ return bytes;
+ }
+
+ public byte[] encode(int value) throws Pack200Exception {
+ return encode(value, 0);
+ }
+
+ /**
+ * Returns true if this codec is a delta codec
+ *
+ * @return true if this codec is a delta codec
+ */
+ public boolean isDelta() {
+ return d != 0;
+ }
+
+ /**
+ * Returns true if this codec is a signed codec
+ *
+ * @return true if this codec is a signed codec
+ */
+ public boolean isSigned() {
+ return s != 0;
+ }
+
+ /**
+ * Returns the largest value that this codec can represent.
+ *
+ * @return the largest value that this codec can represent.
+ */
+ public long largest() {
+ return largest;
+ }
+
+ private long calculateLargest() {
+ long result;
+ // TODO This can probably be optimized into a better mathematical
+ // statement
+ if (d == 1) {
+ BHSDCodec bh0 = new BHSDCodec(b, h);
+ return bh0.largest();
+ } else if (s == 0) {
+ result = cardinality() - 1;
+ } else if (s == 1) {
+ result = cardinality() / 2 - 1;
+ } else if (s == 2) {
+ result = (3L * cardinality()) / 4 - 1;
+ } else {
+ throw new Error("Unknown s value");
+ }
+ return Math.min((s == 0 ? ((long) Integer.MAX_VALUE) << 1
+ : Integer.MAX_VALUE) - 1, result);
+ }
+
+ /**
+ * Returns the smallest value that this codec can represent.
+ *
+ * @return the smallest value that this codec can represent.
+ */
+ public long smallest() {
+ return smallest;
+ }
+
+ private long calculateSmallest() {
+ long result;
+ if (d == 1 || !isSigned()) {
+ if (cardinality >= 4294967296L) { // 2^32
+ result = Integer.MIN_VALUE;
+ } else {
+ result = 0;
+ }
+ } else {
+ result = Math.max(Integer.MIN_VALUE, -cardinality() / (1 << s));
+ }
+ return result;
+ }
+
+ /**
+ * Returns the codec in the form (1,256) or (1,64,1,1). Note that trailing
+ * zero fields are not shown.
+ */
+ public String toString() {
+ StringBuffer buffer = new StringBuffer(11);
+ buffer.append('(');
+ buffer.append(b);
+ buffer.append(',');
+ buffer.append(h);
+ if (s != 0 || d != 0) {
+ buffer.append(',');
+ buffer.append(s);
+ }
+ if (d != 0) {
+ buffer.append(',');
+ buffer.append(d);
+ }
+ buffer.append(')');
+ return buffer.toString();
+ }
+
+ /**
+ * @return the b
+ */
+ public int getB() {
+ return b;
+ }
+
+ /**
+ * @return the h
+ */
+ public int getH() {
+ return h;
+ }
+
+ /**
+ * @return the s
+ */
+ public int getS() {
+ return s;
+ }
+
+ /**
+ * @return the l
+ */
+ public int getL() {
+ return l;
+ }
+
+ public boolean equals(Object o) {
+ if (o instanceof BHSDCodec) {
+ BHSDCodec codec = (BHSDCodec) o;
+ return codec.b == b && codec.h == h && codec.s == s && codec.d == d;
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ return ((b* 37 + h) * 37 + s) * 37 + d;
+ }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/BandSet.java b/src/main/java/org/apache/harmony/pack200/BandSet.java
new file mode 100644
index 0000000..c4f573a
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/BandSet.java
@@ -0,0 +1,788 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract superclass for a set of bands
+ */
+public abstract class BandSet {
+
+ protected final SegmentHeader segmentHeader;
+ final int effort;
+
+ // Minimum size of band for each effort level where we consider alternative codecs
+ // Note: these values have been tuned - please test carefully if changing them
+ private static final int[] effortThresholds = new int[] {0, 0, 1000, 500, 100, 100, 100, 100, 100, 0};
+
+ private long[] canonicalLargest;
+ private long[] canonicalSmallest;
+
+ /**
+ * Create a new BandSet
+ * @param effort - the packing effort to be used (must be 1-9)
+ * @param header - the segment header
+ */
+ public BandSet(int effort, SegmentHeader header) {
+ this.effort = effort;
+ this.segmentHeader = header;
+ }
+
+ /**
+ * Write the packed set of bands to the given output stream
+ * @param out
+ * @throws IOException
+ * @throws Pack200Exception
+ */
+ public abstract void pack(OutputStream out) throws IOException, Pack200Exception;
+
+ /**
+ * Encode a band without considering other Codecs
+ * @param band - the band
+ * @param codec - the Codec to use
+ * @return the encoded band
+ * @throws Pack200Exception
+ */
+ public byte[] encodeScalar(int[] band, BHSDCodec codec) throws Pack200Exception {
+ return codec.encode(band);
+ }
+
+ /**
+ * Encode a single value with the given Codec
+ * @param value - the value to encode
+ * @param codec - Codec to use
+ * @return the encoded value
+ * @throws Pack200Exception
+ */
+ public byte[] encodeScalar(int value, BHSDCodec codec) throws Pack200Exception {
+ return codec.encode(value);
+ }
+
+ /**
+ * Encode a band of integers. The default codec may be used, but other
+ * Codecs are considered if effort is greater than 1.
+ *
+ * @param name
+ * - name of the band (used for debugging)
+ * @param ints
+ * - the band
+ * @param defaultCodec
+ * - the default Codec
+ * @return the encoded band
+ * @throws Pack200Exception
+ */
+ public byte[] encodeBandInt(String name, int[] ints, BHSDCodec defaultCodec) throws Pack200Exception {
+ byte[] encodedBand = null;
+ // Useful for debugging
+// if(ints.length > 0) {
+// System.out.println("encoding " + name + " " + ints.length);
+// }
+ if(effort > 1 && (ints.length >= effortThresholds[effort])) {
+ BandAnalysisResults results = analyseBand(name, ints, defaultCodec);
+ Codec betterCodec = results.betterCodec;
+ encodedBand = results.encodedBand;
+ if(betterCodec != null) {
+ if(betterCodec instanceof BHSDCodec) {
+ int[] specifierBand = CodecEncoding.getSpecifier(betterCodec, defaultCodec);
+ int specifier = specifierBand[0];
+ if(specifierBand.length > 1) {
+ for (int i = 1; i < specifierBand.length; i++) {
+ segmentHeader.appendBandCodingSpecifier(specifierBand[i]);
+ }
+ }
+ if(defaultCodec.isSigned()) {
+ specifier = -1 -specifier;
+ } else {
+ specifier = specifier + defaultCodec.getL();
+ }
+ byte[] specifierEncoded = defaultCodec.encode(new int[] {specifier});
+ byte[] band = new byte[specifierEncoded.length + encodedBand.length];
+ System.arraycopy(specifierEncoded, 0, band, 0, specifierEncoded.length);
+ System.arraycopy(encodedBand, 0, band, specifierEncoded.length, encodedBand.length);
+ return band;
+ } else if (betterCodec instanceof PopulationCodec) {
+ int[] extraSpecifierInfo = results.extraMetadata;
+ for (int i = 0; i < extraSpecifierInfo.length; i++) {
+ segmentHeader.appendBandCodingSpecifier(extraSpecifierInfo[i]);
+ }
+ return encodedBand;
+ } else if (betterCodec instanceof RunCodec) {
+
+ }
+ }
+ }
+
+ // If we get here then we've decided to use the default codec.
+ if(ints.length > 0) {
+ if(encodedBand == null) {
+ encodedBand = defaultCodec.encode(ints);
+ }
+ int first = ints[0];
+ if(defaultCodec.getB() != 1) {
+ if (defaultCodec.isSigned() && first >= -256 && first <= -1) {
+ int specifier = -1 - CodecEncoding.getSpecifierForDefaultCodec(defaultCodec);
+ byte[] specifierEncoded = defaultCodec.encode(new int[] {specifier});
+ byte[] band = new byte[specifierEncoded.length + encodedBand.length];
+ System.arraycopy(specifierEncoded, 0, band, 0, specifierEncoded.length);
+ System.arraycopy(encodedBand, 0, band, specifierEncoded.length, encodedBand.length);
+ return band;
+ } else if (!defaultCodec.isSigned() && first >= defaultCodec.getL()
+ && first <= defaultCodec.getL() + 255) {
+ int specifier = CodecEncoding.getSpecifierForDefaultCodec(defaultCodec) + defaultCodec.getL();
+ byte[] specifierEncoded = defaultCodec.encode(new int[] {specifier});
+ byte[] band = new byte[specifierEncoded.length + encodedBand.length];
+ System.arraycopy(specifierEncoded, 0, band, 0, specifierEncoded.length);
+ System.arraycopy(encodedBand, 0, band, specifierEncoded.length, encodedBand.length);
+ return band;
+ }
+ }
+ return encodedBand;
+ }
+ return new byte[0];
+ }
+
+ private BandAnalysisResults analyseBand(String name, int[] band,
+ BHSDCodec defaultCodec) throws Pack200Exception {
+
+ BandAnalysisResults results = new BandAnalysisResults();
+
+ if(canonicalLargest == null) {
+ canonicalLargest = new long[116];
+ canonicalSmallest = new long[116];
+ for (int i = 1; i < canonicalLargest.length; i++) {
+ canonicalLargest[i] = CodecEncoding.getCanonicalCodec(i).largest();
+ canonicalSmallest[i] = CodecEncoding.getCanonicalCodec(i).smallest();
+ }
+ }
+ BandData bandData = new BandData(band);
+
+ // Check that there is a reasonable saving to be made
+ byte[] encoded = defaultCodec.encode(band);
+ results.encodedBand = encoded;
+
+ // Note: these values have been tuned - please test carefully if changing them
+ if(encoded.length <= band.length + 23 - 2*effort) { // TODO: tweak
+ return results;
+ }
+
+ // Check if we can use BYTE1 as that's a 1:1 mapping if we can
+ if(!bandData.anyNegatives() && bandData.largest <= Codec.BYTE1.largest()) {
+ results.encodedBand = Codec.BYTE1.encode(band) ;
+ results.betterCodec = Codec.BYTE1;
+ return results;
+ }
+
+ // Consider a population codec (but can't be nested)
+ if(effort > 3 && !name.equals("POPULATION")) {
+ int numDistinctValues = bandData.numDistinctValues();
+ float distinctValuesAsProportion = (float)numDistinctValues / (float)band.length;
+
+ // Note: these values have been tuned - please test carefully if changing them
+ if(numDistinctValues < 100 || distinctValuesAsProportion < 0.02 || (effort > 6 && distinctValuesAsProportion < 0.04)) { // TODO: tweak
+ encodeWithPopulationCodec(name, band, defaultCodec, bandData, results);
+ if(timeToStop(results)) {
+ return results;
+ }
+ }
+ }
+
+ List codecFamiliesToTry = new ArrayList();
+
+ // See if the deltas are mainly small increments
+ if(bandData.mainlyPositiveDeltas() && bandData.mainlySmallDeltas()) {
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs2);
+ }
+
+ if (bandData.wellCorrelated()) { // Try delta encodings
+ if (bandData.mainlyPositiveDeltas()) {
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs1);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs3);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs4);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs5);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs1);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs3);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs4);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs5);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs2);
+ } else {
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs1);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs3);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs2);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs4);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs5);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaSignedCodecs1);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaSignedCodecs2);
+ }
+ } else {
+ if (bandData.anyNegatives()) {
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaSignedCodecs1);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaSignedCodecs2);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs1);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs2);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs3);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs4);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs5);
+ } else {
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs1);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs3);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs4);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs5);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs2);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs1);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs3);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs4);
+ codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs5);
+ }
+ }
+ if(name.equalsIgnoreCase("cpint")) {
+ System.out.print("");
+ }
+
+ for (Iterator iterator = codecFamiliesToTry.iterator(); iterator
+ .hasNext();) {
+ BHSDCodec[] family = (BHSDCodec[]) iterator.next();
+ tryCodecs(name, band, defaultCodec, bandData, results, encoded,
+ family);
+ if (timeToStop(results)) {
+ break;
+ }
+ }
+
+ return results;
+ }
+
+ private boolean timeToStop(BandAnalysisResults results) {
+ // if tried more than effort number of codecs for this band then return
+ // Note: these values have been tuned - please test carefully if changing them
+ if(effort > 6) {
+ return results.numCodecsTried >= effort * 2;
+ }
+ return results.numCodecsTried >= effort;
+ // May want to also check how much we've saved if performance needs improving, e.g. saved more than effort*2 %
+ // || (float)results.saved/(float)results.encodedBand.length > (float)effort * 2/100;
+ }
+
+ private void tryCodecs(String name, int[] band, BHSDCodec defaultCodec, BandData bandData,
+ BandAnalysisResults results, byte[] encoded,
+ BHSDCodec[] potentialCodecs) throws Pack200Exception {
+ for (int i = 0; i < potentialCodecs.length; i++) {
+ BHSDCodec potential = potentialCodecs[i];
+ if(potential.equals(defaultCodec)) {
+ return; // don't try codecs with greater cardinality in the same 'family' as the default codec as there won't be any savings
+ }
+ if (potential.isDelta()) {
+ if (potential.largest() >= bandData.largestDelta
+ && potential.smallest() <= bandData.smallestDelta
+ && potential.largest() >= bandData.largest
+ && potential.smallest() <= bandData.smallest) {
+ // TODO: can represent some negative deltas with overflow
+ byte[] encoded2 = potential.encode(band);
+ results.numCodecsTried++;
+ byte[] specifierEncoded = defaultCodec.encode(CodecEncoding
+ .getSpecifier(potential, null));
+ int saved = encoded.length - encoded2.length
+ - specifierEncoded.length;
+ if (saved > results.saved) {
+ results.betterCodec = potential;
+ results.encodedBand = encoded2;
+ results.saved = saved;
+ }
+ }
+ } else if (potential.largest() >= bandData.largest
+ && potential.smallest() <= bandData.smallest) {
+ byte[] encoded2 = potential.encode(band);
+ results.numCodecsTried++;
+ byte[] specifierEncoded = defaultCodec.encode(CodecEncoding
+ .getSpecifier(potential, null));
+ int saved = encoded.length - encoded2.length
+ - specifierEncoded.length;
+ if (saved > results.saved) {
+ results.betterCodec = potential;
+ results.encodedBand = encoded2;
+ results.saved = saved;
+ }
+ }
+ if(timeToStop(results)) {
+ return;
+ }
+ }
+ }
+
+// This could be useful if further enhancements are done but is not currently used
+//
+// private void encodeWithRunCodec(String name, int[] band, int index,
+// BHSDCodec defaultCodec, BandData bandData,
+// BandAnalysisResults results) throws Pack200Exception {
+// int[] firstBand = new int[index];
+// int[] secondBand = new int[band.length - index];
+// System.arraycopy(band, 0, firstBand, 0, index);
+// System.arraycopy(band, index, secondBand, 0, secondBand.length);
+// BandAnalysisResults firstResults = analyseBand(name + "A", firstBand, defaultCodec);
+// BandAnalysisResults secondResults = analyseBand(name + "B", secondBand, defaultCodec);
+// int specifier = 117;
+// byte[] specifierEncoded = defaultCodec.encode(new int[] {specifier});
+// int totalLength = firstResults.encodedBand.length + secondResults.encodedBand.length + specifierEncoded.length + 4; // TODO actual
+// if(totalLength < results.encodedBand.length) {
+// System.out.println("using run codec");
+// results.saved += results.encodedBand.length - totalLength;
+// byte[] encodedBand = new byte[specifierEncoded.length + firstResults.encodedBand.length + secondResults.encodedBand.length];
+// System.arraycopy(specifierEncoded, 0, encodedBand, 0, specifierEncoded.length);
+// System.arraycopy(firstResults.encodedBand, 0, encodedBand, specifierEncoded.length, firstResults.encodedBand.length);
+// System.arraycopy(secondResults.encodedBand, 0, encodedBand, specifierEncoded.length + firstResults.encodedBand.length, secondResults.encodedBand.length);
+// results.encodedBand = encodedBand;
+// results.betterCodec = new RunCodec(index, firstResults.betterCodec, secondResults.betterCodec);
+// }
+// }
+
+ private void encodeWithPopulationCodec(String name, int[] band,
+ BHSDCodec defaultCodec, BandData bandData, BandAnalysisResults results) throws Pack200Exception {
+ results.numCodecsTried += 3; // quite a bit more effort to try this codec
+ final Map distinctValues = bandData.distinctValues;
+
+ List favoured = new ArrayList();
+ for (Iterator iterator = distinctValues.keySet().iterator(); iterator
+ .hasNext();) {
+ Integer value = (Integer) iterator.next();
+ Integer count = (Integer) distinctValues.get(value);
+ if(count.intValue() > 2 || distinctValues.size() < 256) { // TODO: tweak
+ favoured.add(value);
+ }
+ }
+
+ // Sort the favoured list with the most commonly occurring first
+ if(distinctValues.size() > 255) {
+ Collections.sort(favoured, new Comparator() {
+ public int compare(Object arg0, Object arg1) {
+ return ((Integer)distinctValues.get(arg1)).compareTo((Integer)distinctValues.get(arg0));
+ }
+ });
+ }
+
+ IntList unfavoured = new IntList();
+ Map favouredToIndex = new HashMap();
+ for (int i = 0; i < favoured.size(); i++) {
+ Integer value = (Integer) favoured.get(i);
+ favouredToIndex.put(value, new Integer(i));
+ }
+
+ int[] tokens = new int[band.length];
+ for (int i = 0; i < band.length; i++) {
+ Integer favouredIndex = (Integer)favouredToIndex.get(new Integer(band[i]));
+ if(favouredIndex == null) {
+ tokens[i] = 0;
+ unfavoured.add(band[i]);
+ } else {
+ tokens[i] = favouredIndex.intValue() + 1;
+ }
+ }
+ favoured.add(favoured.get(favoured.size() - 1)); // repeat last value
+ int[] favouredBand = integerListToArray(favoured);
+ int[] unfavouredBand = unfavoured.toArray();
+
+ // Analyse the three bands to get the best codec
+ BandAnalysisResults favouredResults = analyseBand("POPULATION", favouredBand, defaultCodec);
+ BandAnalysisResults unfavouredResults = analyseBand("POPULATION", unfavouredBand, defaultCodec);
+
+ int tdefL = 0;
+ int l = 0;
+ Codec tokenCodec = null;
+ byte[] tokensEncoded;
+ int k = favoured.size() - 1;
+ if(k < 256) {
+ tdefL = 1;
+ tokensEncoded = Codec.BYTE1.encode(tokens);
+ } else {
+ BandAnalysisResults tokenResults = analyseBand("POPULATION", tokens, defaultCodec);
+ tokenCodec = tokenResults.betterCodec;
+ tokensEncoded = tokenResults.encodedBand;
+ if(tokenCodec == null) {
+ tokenCodec = defaultCodec;
+ }
+ l = ((BHSDCodec) tokenCodec).getL();
+ int h = ((BHSDCodec) tokenCodec).getH();
+ int s = ((BHSDCodec) tokenCodec).getS();
+ int b = ((BHSDCodec) tokenCodec).getB();
+ int d = ((BHSDCodec) tokenCodec).isDelta() ? 1 : 0;
+ if(s == 0 && d == 0) {
+ boolean canUseTDefL = true;
+ if(b > 1) {
+ BHSDCodec oneLowerB = new BHSDCodec(b-1, h);
+ if(oneLowerB.largest() >= k) {
+ canUseTDefL = false;
+ }
+ }
+ if(canUseTDefL) {
+ switch (l) {
+ case 4:
+ tdefL = 1;
+ break;
+ case 8:
+ tdefL = 2;
+ break;
+ case 16:
+ tdefL = 3;
+ break;
+ case 32:
+ tdefL = 4;
+ break;
+ case 64:
+ tdefL = 5;
+ break;
+ case 128:
+ tdefL = 6;
+ break;
+ case 192:
+ tdefL = 7;
+ break;
+ case 224:
+ tdefL = 8;
+ break;
+ case 240:
+ tdefL = 9;
+ break;
+ case 248:
+ tdefL = 10;
+ break;
+ case 252:
+ tdefL = 11;
+ break;
+ }
+ }
+ }
+ }
+
+ byte[] favouredEncoded = favouredResults.encodedBand;
+ byte[] unfavouredEncoded = unfavouredResults.encodedBand;
+ Codec favouredCodec = favouredResults.betterCodec;
+ Codec unfavouredCodec = unfavouredResults.betterCodec;
+
+ int specifier = 141 + (favouredCodec == null ? 1 : 0) + (4 * tdefL) + (unfavouredCodec == null ? 2 : 0);
+ IntList extraBandMetadata = new IntList(3);
+ if(favouredCodec != null) {
+ int[] specifiers = CodecEncoding.getSpecifier(favouredCodec, null);
+ for (int i = 0; i < specifiers.length; i++) {
+ extraBandMetadata.add(specifiers[i]);
+ }
+ }
+ if(tdefL == 0) {
+ int[] specifiers = CodecEncoding.getSpecifier(tokenCodec, null);
+ for (int i = 0; i < specifiers.length; i++) {
+ extraBandMetadata.add(specifiers[i]);
+ }
+ }
+ if(unfavouredCodec != null) {
+ int[] specifiers = CodecEncoding.getSpecifier(unfavouredCodec, null);
+ for (int i = 0; i < specifiers.length; i++) {
+ extraBandMetadata.add(specifiers[i]);
+ }
+ }
+ int[] extraMetadata = extraBandMetadata.toArray();
+ byte[] extraMetadataEncoded = Codec.UNSIGNED5.encode(extraMetadata);
+ if(defaultCodec.isSigned()) {
+ specifier = -1 -specifier;
+ } else {
+ specifier = specifier + defaultCodec.getL();
+ }
+ byte[] firstValueEncoded = defaultCodec.encode(new int[] {specifier});
+ int totalBandLength = firstValueEncoded.length + favouredEncoded.length + tokensEncoded.length + unfavouredEncoded.length;
+
+ if(totalBandLength + extraMetadataEncoded.length < results.encodedBand.length) {
+ results.saved += results.encodedBand.length - (totalBandLength + extraMetadataEncoded.length);
+ byte[] encodedBand = new byte[totalBandLength];
+ System.arraycopy(firstValueEncoded, 0, encodedBand, 0, firstValueEncoded.length);
+ System.arraycopy(favouredEncoded, 0, encodedBand, firstValueEncoded.length, favouredEncoded.length);
+ System.arraycopy(tokensEncoded, 0, encodedBand, firstValueEncoded.length + favouredEncoded.length, tokensEncoded.length);
+ System.arraycopy(unfavouredEncoded, 0, encodedBand, firstValueEncoded.length + favouredEncoded.length + tokensEncoded.length, unfavouredEncoded.length);
+ results.encodedBand = encodedBand;
+ results.extraMetadata = extraMetadata;
+ if(l != 0) {
+ results.betterCodec = new PopulationCodec(favouredCodec, l, unfavouredCodec);
+ } else {
+ results.betterCodec = new PopulationCodec(favouredCodec, tokenCodec, unfavouredCodec);
+ }
+ }
+ }
+
+ /**
+ * Encode a band of longs (values are split into their high and low 32 bits
+ * and then encoded as two separate bands
+ *
+ * @param name
+ * - name of the band (for debugging purposes)
+ * @param flags
+ * - the band
+ * @param loCodec
+ * - Codec for the low 32-bits band
+ * @param hiCodec
+ * - Codec for the high 32-bits band
+ * @param haveHiFlags
+ * - ignores the high band if true as all values would be zero
+ * @return the encoded band
+ * @throws Pack200Exception
+ */
+ protected byte[] encodeFlags(String name, long[] flags, BHSDCodec loCodec, BHSDCodec hiCodec,
+ boolean haveHiFlags) throws Pack200Exception {
+ if(!haveHiFlags) {
+ int[] loBits = new int[flags.length];
+ for (int i = 0; i < flags.length; i++) {
+ loBits[i] = (int) flags[i];
+ }
+ return encodeBandInt(name, loBits, loCodec);
+ } else {
+
+ int[] hiBits = new int[flags.length];
+ int[] loBits = new int[flags.length];
+ for (int i = 0; i < flags.length; i++) {
+ long l = flags[i];
+ hiBits[i] = (int) (l >> 32);
+ loBits[i] = (int) l;
+ }
+ byte[] hi = encodeBandInt(name, hiBits, hiCodec);
+ byte[] lo = encodeBandInt(name, loBits, loCodec);
+ byte[] total = new byte[hi.length + lo.length];
+ System.arraycopy(hi, 0, total, 0, hi.length);
+ System.arraycopy(lo, 0, total, hi.length + 1, lo.length);
+ return total;
+ }
+ }
+
+ /**
+ * Converts a list of Integers to an int[] array
+ */
+ protected int[] integerListToArray(List integerList) {
+ int[] array = new int[integerList.size()];
+ for (int i = 0; i < array.length; i++) {
+ array[i] = ((Integer)integerList.get(i)).intValue();
+ }
+ return array;
+ }
+
+ /**
+ * Converts a list of Longs to an long[] array
+ */
+ protected long[] longListToArray(List longList) {
+ long[] array = new long[longList.size()];
+ for (int i = 0; i < array.length; i++) {
+ array[i] = ((Long)longList.get(i)).longValue();
+ }
+ return array;
+ }
+
+ /**
+ * Converts a list of ConstantPoolEntrys to an int[] array of their indices
+ */
+ protected int[] cpEntryListToArray(List list) {
+ int[] array = new int[list.size()];
+ for (int i = 0; i < array.length; i++) {
+ array[i] = ((ConstantPoolEntry)list.get(i)).getIndex();
+ if(array[i] < 0) {
+ throw new RuntimeException("Index should be > 0");
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Converts a list of ConstantPoolEntrys or nulls to an int[] array of their
+ * indices +1 (or 0 for nulls)
+ */
+ protected int[] cpEntryOrNullListToArray(List theList) {
+ int[] array = new int[theList.size()];
+ for (int j = 0; j < array.length; j++) {
+ ConstantPoolEntry cpEntry = (ConstantPoolEntry) theList.get(j);
+ array[j] = cpEntry == null ? 0 : cpEntry.getIndex() + 1;
+ if(cpEntry != null && cpEntry.getIndex() < 0) {
+ throw new RuntimeException("Index should be > 0");
+ }
+ }
+ return array;
+ }
+
+ protected byte[] encodeFlags(String name, long[][] flags, BHSDCodec loCodec, BHSDCodec hiCodec,
+ boolean haveHiFlags) throws Pack200Exception {
+ return encodeFlags(name, flatten(flags), loCodec, hiCodec, haveHiFlags);
+ }
+
+ /*
+ * Flatten a 2-dimension array into a 1-dimension array
+ */
+ private long[] flatten(long[][] flags) {
+ int totalSize = 0;
+ for (int i = 0; i < flags.length; i++) {
+ totalSize += flags[i].length;
+ }
+ long[] flatArray = new long[totalSize];
+ int index = 0;
+ for (int i = 0; i < flags.length; i++) {
+ for (int j = 0; j < flags[i].length; j++) {
+ flatArray[index] = flags[i][j];
+ index++;
+ }
+ }
+ return flatArray;
+ }
+
+ /**
+ * BandData represents information about a band, e.g. largest value etc
+ * and is used in the heuristics that calculate whether an alternative
+ * Codec could make the encoded band smaller.
+ */
+ public class BandData {
+
+ private final int[] band;
+ private int smallest = Integer.MAX_VALUE;
+ private int largest = Integer.MIN_VALUE;
+ private int smallestDelta;
+ private int largestDelta;
+
+ private int deltaIsAscending = 0;
+ private int smallDeltaCount = 0;
+
+ private double averageAbsoluteDelta = 0;
+ private double averageAbsoluteValue = 0;
+
+ private Map distinctValues;
+
+ /**
+ * Create a new instance of BandData. The band is then analysed.
+ * @param band - the band of integers
+ */
+ public BandData(int[] band) {
+ this.band = band;
+ Integer one = new Integer(1);
+ for (int i = 0; i < band.length; i++) {
+ if(band[i] < smallest) {
+ smallest = band[i];
+ }
+ if(band[i] > largest) {
+ largest = band[i];
+ }
+ if(i != 0) {
+ int delta = band[i] - band[i - 1];
+ if(delta < smallestDelta) {
+ smallestDelta = delta;
+ }
+ if(delta > largestDelta) {
+ largestDelta = delta;
+ }
+ if(delta >= 0) {
+ deltaIsAscending++;
+ }
+ averageAbsoluteDelta += (double)Math.abs(delta)/(double)(band.length - 1);
+ if(Math.abs(delta) < 256) {
+ smallDeltaCount++;
+ }
+ } else {
+ smallestDelta = band[0];
+ largestDelta = band[0];
+ }
+ averageAbsoluteValue += (double)Math.abs(band[i])/(double)band.length;
+ if(effort > 3) { // do calculations needed to consider population codec
+ if(distinctValues == null) {
+ distinctValues = new HashMap();
+ }
+ Integer value = new Integer(band[i]);
+ Integer count = (Integer) distinctValues.get(value);
+ if(count == null) {
+ count = one;
+ } else {
+ count = new Integer(count.intValue() + 1);
+ }
+ distinctValues.put(value, count);
+ }
+ }
+ }
+
+ /**
+ * Returns true if the deltas between adjacent band elements are mainly
+ * small (heuristic)
+ */
+ public boolean mainlySmallDeltas() {
+ // Note: the value below has been tuned - please test carefully if changing it
+ return (float)smallDeltaCount/(float)band.length > 0.7F;
+ }
+
+ /**
+ * Returns true if the band is well correlated (i.e. would be suitable
+ * for a delta encoding) (heuristic)
+ */
+ public boolean wellCorrelated() {
+ // Note: the value below has been tuned - please test carefully if changing it
+ return averageAbsoluteDelta * 3.1 < averageAbsoluteValue;
+ }
+
+ /**
+ * Returns true if the band deltas are mainly positive (heuristic)
+ */
+ public boolean mainlyPositiveDeltas() {
+ // Note: the value below has been tuned - please test carefully if changing it
+ return (float)deltaIsAscending/(float)band.length > 0.95F;
+ }
+
+ /**
+ * Returns true if any band elements are negative
+ */
+ public boolean anyNegatives() {
+ return smallest < 0;
+ }
+
+ /**
+ * Returns the total number of distinct values found in the band
+ */
+ public int numDistinctValues() {
+ if(distinctValues == null) {
+ return band.length;
+ }
+ return distinctValues.size();
+ }
+
+ }
+
+ /**
+ * Results obtained by trying different Codecs to encode a band
+ */
+ public class BandAnalysisResults {
+
+ // The number of Codecs tried so far
+ private int numCodecsTried = 0;
+
+ // The number of bytes saved by using betterCodec instead of the default codec
+ private int saved = 0;
+
+ // Extra metadata to pass to the segment header (to be appended to the
+ // band_headers band)
+ private int[] extraMetadata;
+
+ // The results of encoding the band with betterCodec
+ private byte[] encodedBand;
+
+ // The best Codec found so far, or should be null if the default is the
+ // best so far
+ private Codec betterCodec;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/BcBands.java b/src/main/java/org/apache/harmony/pack200/BcBands.java
new file mode 100644
index 0000000..92c65fe
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/BcBands.java
@@ -0,0 +1,583 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.objectweb.asm.Label;
+
+/**
+ * Bytecode bands (corresponds to the <code>bc_bands</code> set of bands in the
+ * pack200 specification)
+ */
+public class BcBands extends BandSet {
+
+ private final CpBands cpBands;
+ private final Segment segment;
+
+ public BcBands(CpBands cpBands, Segment segment, int effort) {
+ super(effort, segment.getSegmentHeader());
+ this.cpBands = cpBands;
+ this.segment = segment;
+ }
+
+ private final IntList bcCodes = new IntList();
+ private final IntList bcCaseCount = new IntList();
+ private final IntList bcCaseValue = new IntList();
+ private final IntList bcByte = new IntList();
+ private final IntList bcShort = new IntList();
+ private final IntList bcLocal = new IntList();
+ private final List bcLabel = new ArrayList();
+ private final List bcIntref = new ArrayList();
+ private final List bcFloatRef = new ArrayList();
+ private final List bcLongRef = new ArrayList();
+ private final List bcDoubleRef = new ArrayList();
+ private final List bcStringRef = new ArrayList();
+ private final List bcClassRef = new ArrayList();
+ private final List bcFieldRef = new ArrayList();
+ private final List bcMethodRef = new ArrayList();
+ private final List bcIMethodRef = new ArrayList();
+ private List bcThisField = new ArrayList();
+ private final List bcSuperField = new ArrayList();
+ private List bcThisMethod = new ArrayList();
+ private List bcSuperMethod = new ArrayList();
+ private List bcInitRef = new ArrayList();
+
+ private String currentClass;
+ private String superClass;
+ private String currentNewClass;
+
+ private static final int MULTIANEWARRAY = 197;
+ private static final int ALOAD_0 = 42;
+ private static final int WIDE = 196;
+ private static final int INVOKEINTERFACE = 185;
+ private static final int TABLESWITCH = 170;
+ private static final int IINC = 132;
+ private static final int LOOKUPSWITCH = 171;
+ private static final int endMarker = 255;
+
+ private final IntList bciRenumbering = new IntList();
+ private final Map labelsToOffsets = new HashMap();
+ private int byteCodeOffset;
+ private int renumberedOffset;
+ private final IntList bcLabelRelativeOffsets = new IntList();
+
+ public void setCurrentClass(String name, String superName) {
+ currentClass = name;
+ superClass = superName;
+ }
+
+ /**
+ * All input classes for the segment have now been read in, so this method
+ * is called so that this class can calculate/complete anything it could not
+ * do while classes were being read.
+ */
+ public void finaliseBands() {
+ bcThisField = getIndexInClass(bcThisField);
+ bcThisMethod = getIndexInClass(bcThisMethod);
+ bcSuperMethod = getIndexInClass(bcSuperMethod);
+ bcInitRef = getIndexInClassForConstructor(bcInitRef);
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ PackingUtils.log("Writing byte code bands...");
+ byte[] encodedBand = encodeBandInt("bcCodes", bcCodes.toArray(),
+ Codec.BYTE1);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcCodes["
+ + bcCodes.size() + "]");
+
+ encodedBand = encodeBandInt("bcCaseCount", bcCaseCount.toArray(),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcCaseCount[" + bcCaseCount.size() + "]");
+
+ encodedBand = encodeBandInt("bcCaseValue", bcCaseValue.toArray(),
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcCaseValue[" + bcCaseValue.size() + "]");
+
+ encodedBand = encodeBandInt("bcByte", bcByte.toArray(), Codec.BYTE1);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcByte["
+ + bcByte.size() + "]");
+
+ encodedBand = encodeBandInt("bcShort", bcShort.toArray(), Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcShort["
+ + bcShort.size() + "]");
+
+ encodedBand = encodeBandInt("bcLocal", bcLocal.toArray(),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcLocal["
+ + bcLocal.size() + "]");
+
+ encodedBand = encodeBandInt("bcLabel", integerListToArray(bcLabel),
+ Codec.BRANCH5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcLabel["
+ + bcLabel.size() + "]");
+
+ encodedBand = encodeBandInt("bcIntref", cpEntryListToArray(bcIntref),
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcIntref[" + bcIntref.size() + "]");
+
+ encodedBand = encodeBandInt("bcFloatRef",
+ cpEntryListToArray(bcFloatRef), Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcFloatRef[" + bcFloatRef.size() + "]");
+
+ encodedBand = encodeBandInt("bcLongRef", cpEntryListToArray(bcLongRef),
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcLongRef[" + bcLongRef.size() + "]");
+
+ encodedBand = encodeBandInt("bcDoubleRef",
+ cpEntryListToArray(bcDoubleRef), Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcDoubleRef[" + bcDoubleRef.size() + "]");
+
+ encodedBand = encodeBandInt("bcStringRef",
+ cpEntryListToArray(bcStringRef), Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcStringRef[" + bcStringRef.size() + "]");
+
+ encodedBand = encodeBandInt("bcClassRef",
+ cpEntryOrNullListToArray(bcClassRef), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcClassRef[" + bcClassRef.size() + "]");
+
+ encodedBand = encodeBandInt("bcFieldRef",
+ cpEntryListToArray(bcFieldRef), Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcFieldRef[" + bcFieldRef.size() + "]");
+
+ encodedBand = encodeBandInt("bcMethodRef",
+ cpEntryListToArray(bcMethodRef), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcMethodRef[" + bcMethodRef.size() + "]");
+
+ encodedBand = encodeBandInt("bcIMethodRef",
+ cpEntryListToArray(bcIMethodRef), Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcIMethodRef[" + bcIMethodRef.size() + "]");
+
+ encodedBand = encodeBandInt("bcThisField",
+ integerListToArray(bcThisField), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcThisField[" + bcThisField.size() + "]");
+
+ encodedBand = encodeBandInt("bcSuperField",
+ integerListToArray(bcSuperField), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcSuperField[" + bcSuperField.size() + "]");
+
+ encodedBand = encodeBandInt("bcThisMethod",
+ integerListToArray(bcThisMethod), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcThisMethod[" + bcThisMethod.size() + "]");
+
+ encodedBand = encodeBandInt("bcSuperMethod",
+ integerListToArray(bcSuperMethod), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcSuperMethod[" + bcSuperMethod.size() + "]");
+
+ encodedBand = encodeBandInt("bcInitRef", integerListToArray(bcInitRef),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from bcInitRef[" + bcInitRef.size() + "]");
+
+ // out.write(encodeBandInt(cpEntryintegerListToArray(bcEscRef),
+ // Codec.UNSIGNED5));
+ // out.write(encodeBandInt(integerListToArray(bcEscRefSize),
+ // Codec.UNSIGNED5));
+ // out.write(encodeBandInt(integerListToArray(bcEscSize),
+ // Codec.UNSIGNED5));
+ // out.write(encodeBandInt(integerListToArray(bcEscByte), Codec.BYTE1));
+ }
+
+ private List getIndexInClass(List cPMethodOrFieldList) {
+ List indices = new ArrayList(cPMethodOrFieldList.size());
+ for (int i = 0; i < cPMethodOrFieldList.size(); i++) {
+ CPMethodOrField cpMF = (CPMethodOrField) cPMethodOrFieldList.get(i);
+ indices.add(new Integer(cpMF.getIndexInClass()));
+ }
+ return indices;
+ }
+
+ private List getIndexInClassForConstructor(List cPMethodList) {
+ List indices = new ArrayList(cPMethodList.size());
+ for (int i = 0; i < cPMethodList.size(); i++) {
+ CPMethodOrField cpMF = (CPMethodOrField) cPMethodList.get(i);
+ indices.add(new Integer(cpMF.getIndexInClassForConstructor()));
+ }
+ return indices;
+ }
+
+ public void visitEnd() {
+ for (int i = 0; i < bciRenumbering.size(); i++) {
+ if (bciRenumbering.get(i) == -1) {
+ bciRenumbering.remove(i);
+ bciRenumbering.add(i, ++renumberedOffset);
+ }
+ }
+ if (renumberedOffset != 0) {
+ if(renumberedOffset + 1 != bciRenumbering.size()) {
+ throw new RuntimeException("Mistake made with renumbering");
+ }
+ for (int i = bcLabel.size() - 1; i >= 0; i--) {
+ Object label = bcLabel.get(i);
+ if (label instanceof Integer) {
+ break;
+ } else if (label instanceof Label) {
+ bcLabel.remove(i);
+ Integer offset = (Integer) labelsToOffsets.get(label);
+ int relativeOffset = bcLabelRelativeOffsets.get(i);
+ bcLabel.add(i, new Integer(bciRenumbering.get(offset.intValue()) - bciRenumbering.get(relativeOffset)));
+ }
+ }
+ bcCodes.add(endMarker);
+ segment.getClassBands().doBciRenumbering(bciRenumbering,
+ labelsToOffsets);
+ bciRenumbering.clear();
+ labelsToOffsets.clear();
+ byteCodeOffset = 0;
+ renumberedOffset = 0;
+ }
+ }
+
+ public void visitLabel(Label label) {
+ labelsToOffsets.put(label, new Integer(byteCodeOffset));
+ }
+
+ public void visitFieldInsn(int opcode, String owner, String name,
+ String desc) {
+ byteCodeOffset += 3;
+ updateRenumbering();
+ boolean aload_0 = false;
+ if (bcCodes.size() > 0
+ && (bcCodes.get(bcCodes.size() - 1)) == ALOAD_0) {
+ bcCodes.remove(bcCodes.size() - 1);
+ aload_0 = true;
+ }
+ CPMethodOrField cpField = cpBands.getCPField(owner, name, desc);
+ if (aload_0) {
+ opcode += 7;
+ }
+ if (owner.equals(currentClass)) {
+ opcode += 24; // change to getstatic_this, putstatic_this etc.
+ bcThisField.add(cpField);
+// } else if (owner.equals(superClass)) {
+// opcode += 38; // change to getstatic_super etc.
+// bcSuperField.add(cpField);
+ } else {
+ if (aload_0) {
+ opcode -= 7;
+ bcCodes.add(ALOAD_0); // add aload_0 back in because
+ // there's no special rewrite in
+ // this case.
+ }
+ bcFieldRef.add(cpField);
+ }
+ aload_0 = false;
+ bcCodes.add(opcode);
+ }
+
+ private void updateRenumbering() {
+ if(bciRenumbering.isEmpty()) {
+ bciRenumbering.add(0);
+ }
+ renumberedOffset ++;
+ for (int i = bciRenumbering.size(); i < byteCodeOffset; i++) {
+ bciRenumbering.add(-1);
+ }
+ bciRenumbering.add(renumberedOffset);
+ }
+
+ public void visitIincInsn(int var, int increment) {
+ if (var > 255 || increment > 255) {
+ byteCodeOffset += 6;
+ bcCodes.add(WIDE);
+ bcCodes.add(IINC);
+ bcLocal.add(var);
+ bcShort.add(increment);
+ } else {
+ byteCodeOffset += 3;
+ bcCodes.add(IINC);
+ bcLocal.add(var);
+ bcByte.add(increment & 0xFF);
+ }
+ updateRenumbering();
+ }
+
+ public void visitInsn(int opcode) {
+ if (opcode >= 202) {
+ throw new RuntimeException(
+ "Non-standard bytecode instructions not supported");
+ } else {
+ bcCodes.add(opcode);
+ byteCodeOffset++;
+ updateRenumbering();
+ }
+ }
+
+ public void visitIntInsn(int opcode, int operand) {
+ switch (opcode) {
+ case 17: // sipush
+ bcCodes.add(opcode);
+ bcShort.add(operand);
+ byteCodeOffset += 3;
+ break;
+ case 16: // bipush
+ case 188: // newarray
+ bcCodes.add(opcode);
+ bcByte.add(operand & 0xFF);
+ byteCodeOffset += 2;
+ }
+ updateRenumbering();
+ }
+
+ public void visitJumpInsn(int opcode, Label label) {
+ bcCodes.add(opcode);
+ bcLabel.add(label);
+ bcLabelRelativeOffsets.add(byteCodeOffset);
+ byteCodeOffset += 3;
+ updateRenumbering();
+ }
+
+ public void visitLdcInsn(Object cst) {
+ CPConstant constant = cpBands.getConstant(cst);
+ if (segment.lastConstantHadWideIndex() || constant instanceof CPLong
+ || constant instanceof CPDouble) {
+ byteCodeOffset += 3;
+ if (constant instanceof CPInt) {
+ bcCodes.add(237); // ildc_w
+ bcIntref.add(constant);
+ } else if (constant instanceof CPFloat) {
+ bcCodes.add(238); // fldc
+ bcFloatRef.add(constant);
+ } else if (constant instanceof CPLong) {
+ bcCodes.add(20); // lldc2_w
+ bcLongRef.add(constant);
+ } else if (constant instanceof CPDouble) {
+ bcCodes.add(239); // dldc2_w
+ bcDoubleRef.add(constant);
+ } else if (constant instanceof CPString) {
+ bcCodes.add(19); // aldc
+ bcStringRef.add(constant);
+ } else if (constant instanceof CPClass) {
+ bcCodes.add(236); // cldc
+ bcClassRef.add(constant);
+ } else {
+ throw new RuntimeException("Constant should not be null");
+ }
+ } else {
+ byteCodeOffset += 2;
+ if (constant instanceof CPInt) {
+ bcCodes.add(234); // ildc
+ bcIntref.add(constant);
+ } else if (constant instanceof CPFloat) {
+ bcCodes.add(235); // fldc
+ bcFloatRef.add(constant);
+ } else if (constant instanceof CPString) {
+ bcCodes.add(18); // aldc
+ bcStringRef.add(constant);
+ } else if (constant instanceof CPClass) {
+ bcCodes.add(233); // cldc
+ bcClassRef.add(constant);
+ }
+ }
+ updateRenumbering();
+ }
+
+ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+ bcCodes.add(LOOKUPSWITCH);
+ bcLabel.add(dflt);
+ bcLabelRelativeOffsets.add(byteCodeOffset);
+ bcCaseCount.add(keys.length);
+ for (int i = 0; i < labels.length; i++) {
+ bcCaseValue.add(keys[i]);
+ bcLabel.add(labels[i]);
+ bcLabelRelativeOffsets.add(byteCodeOffset);
+ }
+ int padding = (byteCodeOffset + 1) % 4 == 0 ? 0 : 4 - ((byteCodeOffset + 1) % 4);
+ byteCodeOffset += 1 + padding + 8 + 8 * keys.length;
+ updateRenumbering();
+ }
+
+ public void visitMethodInsn(int opcode, String owner, String name,
+ String desc) {
+ byteCodeOffset += 3;
+ switch (opcode) {
+ case 182: // invokevirtual
+ case 183: // invokespecial
+ case 184: // invokestatic
+ boolean aload_0 = false;
+ if (bcCodes.size() > 0
+ && (bcCodes.get(bcCodes.size() - 1))
+ == (ALOAD_0)) {
+ bcCodes.remove(bcCodes.size() - 1);
+ aload_0 = true;
+ opcode += 7;
+ }
+ if (owner.equals(currentClass)) {
+ opcode += 24; // change to invokevirtual_this,
+ // invokespecial_this etc.
+
+ if(name.equals("<init>") && opcode == 207) {
+ opcode = 230; // invokespecial_this_init
+ bcInitRef.add(cpBands.getCPMethod(owner, name, desc));
+ } else {
+ bcThisMethod.add(cpBands.getCPMethod(owner, name, desc));
+ }
+ } else if (owner.equals(superClass)) { // TODO
+ opcode += 38; // change to invokevirtual_super,
+ // invokespecial_super etc.
+ if(name.equals("<init>") && opcode == 221) {
+ opcode = 231; // invokespecial_super_init
+ bcInitRef.add(cpBands.getCPMethod(owner, name, desc));
+ } else {
+ bcSuperMethod.add(cpBands.getCPMethod(owner, name, desc));
+ }
+ } else {
+ if (aload_0) {
+ opcode -= 7;
+ bcCodes.add(ALOAD_0); // add aload_0 back in
+ // because there's no
+ // special rewrite in this
+ // case.
+ }
+ if(name.equals("<init>") && opcode == 183 && owner.equals(currentNewClass)) {
+ opcode = 232; // invokespecial_new_init
+ bcInitRef.add(cpBands.getCPMethod(owner, name, desc));
+ } else {
+ bcMethodRef.add(cpBands.getCPMethod(owner, name, desc));
+ }
+ }
+ bcCodes.add(opcode);
+ break;
+ case 185: // invokeinterface
+ byteCodeOffset += 2;
+ CPMethodOrField cpIMethod = cpBands.getCPIMethod(owner, name, desc);
+ bcIMethodRef.add(cpIMethod);
+ bcCodes.add(INVOKEINTERFACE);
+ break;
+ }
+ updateRenumbering();
+ }
+
+ public void visitMultiANewArrayInsn(String desc, int dimensions) {
+ byteCodeOffset += 4;
+ updateRenumbering();
+ bcCodes.add(MULTIANEWARRAY);
+ bcClassRef.add(cpBands.getCPClass(desc));
+ bcByte.add(dimensions & 0xFF);
+ }
+
+ public void visitTableSwitchInsn(int min, int max, Label dflt,
+ Label[] labels) {
+ bcCodes.add(TABLESWITCH);
+ bcLabel.add(dflt);
+ bcLabelRelativeOffsets.add(byteCodeOffset);
+ bcCaseValue.add(min);
+ int count = labels.length;
+ bcCaseCount.add(count);
+ for (int i = 0; i < count; i++) {
+ bcLabel.add(labels[i]);
+ bcLabelRelativeOffsets.add(byteCodeOffset);
+ }
+ int padding = byteCodeOffset % 4 == 0 ? 0 : 4 - (byteCodeOffset % 4);
+ byteCodeOffset+= (padding + 12 + 4 * labels.length);
+ updateRenumbering();
+ }
+
+ public void visitTypeInsn(int opcode, String type) {
+ // NEW, ANEWARRAY, CHECKCAST or INSTANCEOF
+ byteCodeOffset += 3;
+ updateRenumbering();
+ bcCodes.add(opcode);
+ bcClassRef.add(cpBands.getCPClass(type));
+ if(opcode == 187) { // NEW
+ currentNewClass = type;
+ }
+ }
+
+ public void visitVarInsn(int opcode, int var) {
+ // ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET
+ if (var > 255) {
+ byteCodeOffset += 4;
+ bcCodes.add(WIDE);
+ bcCodes.add(opcode);
+ bcLocal.add(var);
+ } else {
+ if(var > 3 || opcode == 169 /* RET */) {
+ byteCodeOffset += 2;
+ bcCodes.add(opcode);
+ bcLocal.add(var);
+ } else {
+ byteCodeOffset +=1;
+ switch(opcode) {
+ case 21: // ILOAD
+ case 54: // ISTORE
+ bcCodes.add(opcode + 5 + var);
+ break;
+ case 22: // LLOAD
+ case 55: // LSTORE
+ bcCodes.add(opcode + 8 + var);
+ break;
+ case 23: // FLOAD
+ case 56: // FSTORE
+ bcCodes.add(opcode + 11 + var);
+ break;
+ case 24: // DLOAD
+ case 57: // DSTORE
+ bcCodes.add(opcode + 14 + var);
+ break;
+ case 25: // A_LOAD
+ case 58: // A_STORE
+ bcCodes.add(opcode + 17 + var);
+ break;
+ }
+ }
+ }
+ updateRenumbering();
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPClass.java b/src/main/java/org/apache/harmony/pack200/CPClass.java
new file mode 100644
index 0000000..1639613
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPClass.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Constant pool entry for a class
+ */
+public class CPClass extends CPConstant implements Comparable {
+
+ private final String className;
+ private final CPUTF8 utf8;
+ private final boolean isInnerClass;
+
+ public CPClass(CPUTF8 utf8) {
+ this.utf8 = utf8;
+ this.className = utf8.getUnderlyingString();
+ char[] chars = className.toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ if(chars[i] <= 0x2D) {
+ isInnerClass = true;
+ return;
+ }
+ }
+ isInnerClass = false;
+ }
+
+ public int compareTo(Object arg0) {
+ return className.compareTo(((CPClass)arg0).className);
+ }
+
+ public String toString() {
+ return className;
+ }
+
+ public int getIndexInCpUtf8() {
+ return utf8.getIndex();
+ }
+
+ public boolean isInnerClass() {
+ return isInnerClass;
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPConstant.java b/src/main/java/org/apache/harmony/pack200/CPConstant.java
new file mode 100644
index 0000000..ffd3229
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPConstant.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Abstract superclass for constant pool constant entries such as numbers or
+ * Strings
+ */
+public abstract class CPConstant extends ConstantPoolEntry implements
+ Comparable {
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPDouble.java b/src/main/java/org/apache/harmony/pack200/CPDouble.java
new file mode 100644
index 0000000..210f774
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPDouble.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Constant pool entry for a double.
+ */
+public class CPDouble extends CPConstant {
+
+ private final double theDouble;
+
+ public CPDouble(double theDouble) {
+ this.theDouble = theDouble;
+ }
+
+ public int compareTo(Object obj) {
+ return Double.compare(theDouble, ((CPDouble)obj).theDouble);
+ }
+
+ public double getDouble() {
+ return theDouble;
+ }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPFloat.java b/src/main/java/org/apache/harmony/pack200/CPFloat.java
new file mode 100644
index 0000000..6cb649d
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPFloat.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Constant pool entry for a float.
+ */
+public class CPFloat extends CPConstant {
+
+ private final float theFloat;
+
+ public CPFloat(float theFloat) {
+ this.theFloat = theFloat;
+ }
+
+ public int compareTo(Object obj) {
+ return Float.compare(theFloat, ((CPFloat)obj).theFloat);
+ }
+
+ public float getFloat() {
+ return theFloat;
+ }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPInt.java b/src/main/java/org/apache/harmony/pack200/CPInt.java
new file mode 100644
index 0000000..abcfe30
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPInt.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Constant pool entry for an int.
+ */
+public class CPInt extends CPConstant {
+
+ private final int theInt;
+
+ public CPInt(int theInt) {
+ this.theInt = theInt;
+ }
+
+ public int compareTo(Object obj) {
+ if(theInt > ((CPInt)obj).theInt) {
+ return 1;
+ } else if (theInt == ((CPInt)obj).theInt) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+
+ public int getInt() {
+ return theInt;
+ }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPLong.java b/src/main/java/org/apache/harmony/pack200/CPLong.java
new file mode 100644
index 0000000..2f2d7b4
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPLong.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Constant pool entry for a long.
+ */
+public class CPLong extends CPConstant {
+
+ private final long theLong;
+
+ public CPLong(long theLong) {
+ this.theLong = theLong;
+ }
+
+ public int compareTo(Object obj) {
+ if(theLong > ((CPLong)obj).theLong) {
+ return 1;
+ } else if (theLong == ((CPLong)obj).theLong) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+
+ public long getLong() {
+ return theLong;
+ }
+
+ public String toString() {
+ return "" + theLong;
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPMethodOrField.java b/src/main/java/org/apache/harmony/pack200/CPMethodOrField.java
new file mode 100644
index 0000000..7c1c937
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPMethodOrField.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Constant pool entry for a method or field.
+ */
+public class CPMethodOrField extends ConstantPoolEntry implements Comparable {
+
+ private final CPClass className;
+ private final CPNameAndType nameAndType;
+ private int indexInClass = -1;
+ private int indexInClassForConstructor = -1;
+
+ public CPMethodOrField(CPClass className, CPNameAndType nameAndType) {
+ this.className = className;
+ this.nameAndType = nameAndType;
+ }
+
+ public String toString() {
+ return className + ": " + nameAndType;
+ }
+
+ public int compareTo(Object obj) {
+ if (obj instanceof CPMethodOrField) {
+ CPMethodOrField mof = (CPMethodOrField) obj;
+ int compareName = className.compareTo(mof.className);
+ if (compareName == 0) {
+ return nameAndType.compareTo(mof.nameAndType);
+ } else {
+ return compareName;
+ }
+ }
+ return 0;
+ }
+
+ public int getClassIndex() {
+ return className.getIndex();
+ }
+
+ public CPClass getClassName() {
+ return className;
+ }
+
+ public int getDescIndex() {
+ return nameAndType.getIndex();
+ }
+
+ public CPNameAndType getDesc() {
+ return nameAndType;
+ }
+
+ public int getIndexInClass() {
+ return indexInClass;
+ }
+
+ public void setIndexInClass(int index) {
+ indexInClass = index;
+ }
+
+ public int getIndexInClassForConstructor() {
+ return indexInClassForConstructor;
+ }
+
+ public void setIndexInClassForConstructor(int index) {
+ indexInClassForConstructor = index;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/harmony/pack200/CPNameAndType.java b/src/main/java/org/apache/harmony/pack200/CPNameAndType.java
new file mode 100644
index 0000000..52c3985
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPNameAndType.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Constant pool entry for a name and type pair.
+ */
+public class CPNameAndType extends ConstantPoolEntry implements Comparable {
+
+ private final CPUTF8 name;
+ private final CPSignature signature;
+
+ public CPNameAndType(CPUTF8 name, CPSignature signature) {
+ this.name = name;
+ this.signature = signature;
+ }
+
+ public String toString() {
+ return name + ":" + signature;
+ }
+
+ public int compareTo(Object obj) {
+ if (obj instanceof CPNameAndType) {
+ CPNameAndType nat = (CPNameAndType) obj;
+ int compareSignature = signature.compareTo(nat.signature);;
+ if(compareSignature == 0) {
+ return name.compareTo(nat.name);
+ } else {
+ return compareSignature;
+ }
+ }
+ return 0;
+ }
+
+ public int getNameIndex() {
+ return name.getIndex();
+ }
+
+ public String getName() {
+ return name.getUnderlyingString();
+ }
+
+ public int getTypeIndex() {
+ return signature.getIndex();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/harmony/pack200/CPSignature.java b/src/main/java/org/apache/harmony/pack200/CPSignature.java
new file mode 100644
index 0000000..d5e423c
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPSignature.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.util.List;
+
+/**
+ * Constant pool entry for a signature.
+ */
+public class CPSignature extends ConstantPoolEntry implements Comparable {
+
+ private final CPUTF8 signatureForm;
+ private final List classes;
+ private final String signature;
+ private final boolean formStartsWithBracket;
+
+ public CPSignature(String signature, CPUTF8 signatureForm, List classes) {
+ this.signature = signature;
+ this.signatureForm = signatureForm;
+ this.classes = classes;
+ formStartsWithBracket = signatureForm.toString().startsWith("(");
+ }
+
+ public int compareTo(Object arg0) {
+ if(signature.equals(((CPSignature) arg0).signature)) {
+ return 0;
+ }
+ if (formStartsWithBracket
+ && !((CPSignature) arg0).formStartsWithBracket) {
+ return 1;
+ }
+ if (((CPSignature) arg0).formStartsWithBracket
+ && !formStartsWithBracket) {
+ return -1;
+ }
+ if (classes.size() - ((CPSignature) arg0).classes.size() != 0) {
+ return classes.size() - ((CPSignature) arg0).classes.size();
+ }
+ if (classes.size() > 0) {
+ for (int i = classes.size() - 1; i >=0; i--) {
+ CPClass cpClass = (CPClass) classes.get(i);
+ CPClass compareClass = (CPClass) ((CPSignature) arg0).classes
+ .get(i);
+ int classComp = cpClass.compareTo(compareClass);
+ if(classComp != 0) {
+ return classComp;
+ }
+ }
+ }
+ return signature.compareTo(((CPSignature) arg0).signature);
+ }
+
+ public int getIndexInCpUtf8() {
+ return signatureForm.getIndex();
+ }
+
+ public List getClasses() {
+ return classes;
+ }
+
+ public String toString() {
+ return signature;
+ }
+
+ public String getUnderlyingString() {
+ return signature;
+ }
+
+ public CPUTF8 getSignatureForm() {
+ return signatureForm;
+ }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPString.java b/src/main/java/org/apache/harmony/pack200/CPString.java
new file mode 100644
index 0000000..b8acca6
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPString.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Constant pool entry for a String.
+ */
+public class CPString extends CPConstant {
+
+ private final String string;
+ private final CPUTF8 utf8;
+
+ public CPString(CPUTF8 utf8) {
+ this.utf8 = utf8;
+ this.string = utf8.getUnderlyingString();
+ }
+
+ public int compareTo(Object arg0) {
+ return string.compareTo(((CPString)arg0).string);
+ }
+
+ public String toString() {
+ return string;
+ }
+
+ public int getIndexInCpUtf8() {
+ return utf8.getIndex();
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPUTF8.java b/src/main/java/org/apache/harmony/pack200/CPUTF8.java
new file mode 100644
index 0000000..00a1526
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPUTF8.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Constant pool entry for a UTF8 entry, used for storing long Strings.
+ */
+public class CPUTF8 extends ConstantPoolEntry implements Comparable {
+
+ private final String string;
+
+ public CPUTF8(String string) {
+ this.string = string;
+ }
+
+ public int compareTo(Object arg0) {
+ return string.compareTo(((CPUTF8)arg0).string);
+ }
+
+ public String toString() {
+ return string;
+ }
+
+ public String getUnderlyingString() {
+ return string;
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CanonicalCodecFamilies.java b/src/main/java/org/apache/harmony/pack200/CanonicalCodecFamilies.java
new file mode 100644
index 0000000..174f533
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CanonicalCodecFamilies.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Sets of codecs that share characteristics. Mainly used for different effort
+ * compression heuristics in BandSet.
+ */
+public class CanonicalCodecFamilies {
+
+ // Families of codecs for bands of positive integers that do not correlate
+ // well (i.e. would not benefit from delta encoding)
+
+ public static BHSDCodec[] nonDeltaUnsignedCodecs1 = new BHSDCodec[] {
+ // (1,256) is a special case and is considered separately so shouldn't be included here
+// CodecEncoding.getCanonicalCodec(1), // (1,256)
+ CodecEncoding.getCanonicalCodec(5), // (2,256)
+ CodecEncoding.getCanonicalCodec(9), // (3,256)
+ CodecEncoding.getCanonicalCodec(13) // (4,256)
+ };
+
+ public static BHSDCodec[] nonDeltaUnsignedCodecs2 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(17), // (5,4)
+ CodecEncoding.getCanonicalCodec(20), // (5,16)
+ CodecEncoding.getCanonicalCodec(23), // (5,32)
+ CodecEncoding.getCanonicalCodec(26), // (5,64)
+ CodecEncoding.getCanonicalCodec(29) // (5,128)
+ };
+
+ public static BHSDCodec[] nonDeltaUnsignedCodecs3 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(47), // (2,192)
+ CodecEncoding.getCanonicalCodec(48), // (2,224)
+ CodecEncoding.getCanonicalCodec(49), // (2,240)
+ CodecEncoding.getCanonicalCodec(50), // (2,248)
+ CodecEncoding.getCanonicalCodec(51) // (2,252)
+ };
+
+ public static BHSDCodec[] nonDeltaUnsignedCodecs4 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(70), // (3,192)
+ CodecEncoding.getCanonicalCodec(71), // (3,224)
+ CodecEncoding.getCanonicalCodec(72), // (3,240)
+ CodecEncoding.getCanonicalCodec(73), // (3,248)
+ CodecEncoding.getCanonicalCodec(74) // (3,252)
+ };
+
+ public static BHSDCodec[] nonDeltaUnsignedCodecs5 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(93), // (4,192)
+ CodecEncoding.getCanonicalCodec(94), // (4,224)
+ CodecEncoding.getCanonicalCodec(95), // (4,240)
+ CodecEncoding.getCanonicalCodec(96), // (4,248)
+ CodecEncoding.getCanonicalCodec(97) // (4,252)
+ };
+
+ // Families of codecs for bands of positive integers that do correlate well
+ // and would benefit from delta encoding
+
+ public static BHSDCodec[] deltaUnsignedCodecs1 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(3), // (1,256,0,1)
+ CodecEncoding.getCanonicalCodec(7), // (2,256,0,1)
+ CodecEncoding.getCanonicalCodec(11), // (3,256,0,1)
+ CodecEncoding.getCanonicalCodec(15) // (4,256,0,1)
+ };
+
+ public static BHSDCodec[] deltaUnsignedCodecs2 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(32), // (5,4,0,1)
+ CodecEncoding.getCanonicalCodec(35), // (5,16,0,1)
+ CodecEncoding.getCanonicalCodec(38), // (5,32,0,1)
+ CodecEncoding.getCanonicalCodec(41), // (5,64,0,1)
+ CodecEncoding.getCanonicalCodec(44) // (5,128,0,1)
+ };
+
+ public static BHSDCodec[] deltaUnsignedCodecs3 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(52), // (2,8,0,1)
+ CodecEncoding.getCanonicalCodec(54), // (2,16,0,1)
+ CodecEncoding.getCanonicalCodec(56), // (2,32,0,1)
+ CodecEncoding.getCanonicalCodec(58), // (2,64,0,1)
+ CodecEncoding.getCanonicalCodec(60), // (2,128,0,1)
+ CodecEncoding.getCanonicalCodec(62), // (2,192,0,1)
+ CodecEncoding.getCanonicalCodec(64), // (2,224,0,1)
+ CodecEncoding.getCanonicalCodec(66), // (2,240,0,1)
+ CodecEncoding.getCanonicalCodec(68) // (2,248,0,1)
+ };
+
+ public static BHSDCodec[] deltaUnsignedCodecs4 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(75), // (3,8,0,1)
+ CodecEncoding.getCanonicalCodec(77), // (3,16,0,1)
+ CodecEncoding.getCanonicalCodec(79), // (3,32,0,1)
+ CodecEncoding.getCanonicalCodec(81), // (3,64,0,1)
+ CodecEncoding.getCanonicalCodec(83), // (3,128,0,1)
+ CodecEncoding.getCanonicalCodec(85), // (3,192,0,1)
+ CodecEncoding.getCanonicalCodec(87), // (3,224,0,1)
+ CodecEncoding.getCanonicalCodec(89), // (3,240,0,1)
+ CodecEncoding.getCanonicalCodec(91) // (3,248,0,1)
+ };
+
+ public static BHSDCodec[] deltaUnsignedCodecs5 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(98), // (4,8,0,1)
+ CodecEncoding.getCanonicalCodec(100), // (4,16,0,1)
+ CodecEncoding.getCanonicalCodec(102), // (4,32,0,1)
+ CodecEncoding.getCanonicalCodec(104), // (4,64,0,1)
+ CodecEncoding.getCanonicalCodec(106), // (4,128,0,1)
+ CodecEncoding.getCanonicalCodec(108), // (4,192,0,1)
+ CodecEncoding.getCanonicalCodec(110), // (4,224,0,1)
+ CodecEncoding.getCanonicalCodec(112), // (4,240,0,1)
+ CodecEncoding.getCanonicalCodec(114) // (4,248,0,1)
+ };
+
+ // Families of codecs for bands containing positive and negative integers
+ // that do correlate well (i.e. delta encoding is used)
+
+ public static BHSDCodec[] deltaSignedCodecs1 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(4), // (1,256,1,1)
+ CodecEncoding.getCanonicalCodec(8), // (2,256,1,1)
+ CodecEncoding.getCanonicalCodec(12), // (3,256,1,1)
+ CodecEncoding.getCanonicalCodec(16) // (4,256,1,1)
+ };
+
+ public static BHSDCodec[] deltaSignedCodecs2 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(33), // (5,4,1,1)
+ CodecEncoding.getCanonicalCodec(36), // (5,16,1,1)
+ CodecEncoding.getCanonicalCodec(39), // (5,32,1,1)
+ CodecEncoding.getCanonicalCodec(42), // (5,64,1,1)
+ CodecEncoding.getCanonicalCodec(45) // (5,128,1,1)
+ };
+
+ public static BHSDCodec[] deltaSignedCodecs3 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(53), // (2,8,1,1)
+ CodecEncoding.getCanonicalCodec(55), // (2,16,1,1)
+ CodecEncoding.getCanonicalCodec(57), // (2,32,1,1)
+ CodecEncoding.getCanonicalCodec(59), // (2,64,1,1)
+ CodecEncoding.getCanonicalCodec(61), // (2,128,1,1)
+ CodecEncoding.getCanonicalCodec(63), // (2,192,1,1)
+ CodecEncoding.getCanonicalCodec(65), // (2,224,1,1)
+ CodecEncoding.getCanonicalCodec(67), // (2,240,1,1)
+ CodecEncoding.getCanonicalCodec(69) // (2,248,1,1)
+ };
+
+ public static BHSDCodec[] deltaSignedCodecs4 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(76), // (3,8,1,1)
+ CodecEncoding.getCanonicalCodec(78), // (3,16,1,1)
+ CodecEncoding.getCanonicalCodec(80), // (3,32,1,1)
+ CodecEncoding.getCanonicalCodec(82), // (3,64,1,1)
+ CodecEncoding.getCanonicalCodec(84), // (3,128,1,1)
+ CodecEncoding.getCanonicalCodec(86), // (3,192,1,1)
+ CodecEncoding.getCanonicalCodec(88), // (3,224,1,1)
+ CodecEncoding.getCanonicalCodec(90), // (3,240,1,1)
+ CodecEncoding.getCanonicalCodec(92) // (3,248,1,1)
+ };
+
+ public static BHSDCodec[] deltaSignedCodecs5 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(99), // (4,8,1,1)
+ CodecEncoding.getCanonicalCodec(101), // (4,16,1,1)
+ CodecEncoding.getCanonicalCodec(103), // (4,32,1,1)
+ CodecEncoding.getCanonicalCodec(105), // (4,64,1,1)
+ CodecEncoding.getCanonicalCodec(107), // (4,128,1,1)
+ CodecEncoding.getCanonicalCodec(109), // (4,192,1,1)
+ CodecEncoding.getCanonicalCodec(111), // (4,224,1,1)
+ CodecEncoding.getCanonicalCodec(113), // (4,240,1,1)
+ CodecEncoding.getCanonicalCodec(115) // (4,248,1,1)
+ };
+
+ public static BHSDCodec[] deltaDoubleSignedCodecs1 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(34), // (5,4,2,1)
+ CodecEncoding.getCanonicalCodec(37), // (5,16,2,1)
+ CodecEncoding.getCanonicalCodec(40), // (5,32,2,1)
+ CodecEncoding.getCanonicalCodec(43), // (5,64,2,1)
+ CodecEncoding.getCanonicalCodec(46) // (5,128,2,1)
+ };
+
+ // Families of codecs for bands containing positive and negative values that
+ // do not correlate well (i.e. delta encoding is not used)
+
+ public static BHSDCodec[] nonDeltaSignedCodecs1 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(2), // (1,256,1)
+ CodecEncoding.getCanonicalCodec(6), // (2,256,1)
+ CodecEncoding.getCanonicalCodec(10), // (3,256,1)
+ CodecEncoding.getCanonicalCodec(14) // (4,256,1)
+ };
+
+ public static BHSDCodec[] nonDeltaSignedCodecs2 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(18), // (5,4,1)
+ CodecEncoding.getCanonicalCodec(21), // (5,16,1)
+ CodecEncoding.getCanonicalCodec(24), // (5,32,1)
+ CodecEncoding.getCanonicalCodec(27), // (5,64,1)
+ CodecEncoding.getCanonicalCodec(30) // (5,128,1)
+ };
+
+ public static BHSDCodec[] nonDeltaDoubleSignedCodecs1 = new BHSDCodec[] {
+ CodecEncoding.getCanonicalCodec(19), // (5,4,2)
+ CodecEncoding.getCanonicalCodec(22), // (5,16,2)
+ CodecEncoding.getCanonicalCodec(25), // (5,32,2)
+ CodecEncoding.getCanonicalCodec(28), // (5,64,2)
+ CodecEncoding.getCanonicalCodec(31) // (5,128,2)
+ };
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/ClassBands.java b/src/main/java/org/apache/harmony/pack200/ClassBands.java
new file mode 100644
index 0000000..094a90f
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/ClassBands.java
@@ -0,0 +1,1616 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.harmony.pack200.AttributeDefinitionBands.AttributeDefinition;
+import org.apache.harmony.pack200.IcBands.IcTuple;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Class bands (corresponds to the <code>class_bands</code> set of bands in the
+ * pack200 specification)
+ */
+public class ClassBands extends BandSet {
+
+ private final CpBands cpBands;
+ private final AttributeDefinitionBands attrBands;
+
+ private final CPClass[] class_this;
+ private final CPClass[] class_super;
+ private final CPClass[][] class_interface;
+ private final int[] class_interface_count;
+
+ private final int[] major_versions;
+
+ private final long[] class_flags;
+ private int[] class_attr_calls;
+ private final List classSourceFile = new ArrayList();
+ private final List classEnclosingMethodClass = new ArrayList();
+ private final List classEnclosingMethodDesc = new ArrayList();
+ private final List classSignature = new ArrayList();
+
+ private final IntList classFileVersionMinor = new IntList();
+ private final IntList classFileVersionMajor = new IntList();
+
+ private final int[] class_field_count;
+ private final CPNameAndType[][] field_descr;
+ private final long[][] field_flags;
+ private int[] field_attr_calls;
+ private final List fieldConstantValueKQ = new ArrayList();
+ private final List fieldSignature = new ArrayList();
+
+ private final int[] class_method_count;
+ private final CPNameAndType[][] method_descr;
+ private final long[][] method_flags;
+ private int[] method_attr_calls;
+ private final List methodSignature = new ArrayList();
+ private final IntList methodExceptionNumber = new IntList();
+ private final List methodExceptionClasses = new ArrayList();
+
+ private int[] codeHeaders;
+ private final IntList codeMaxStack = new IntList();
+ private final IntList codeMaxLocals = new IntList();
+ private final IntList codeHandlerCount = new IntList();
+ private final List codeHandlerStartP = new ArrayList();
+ private final List codeHandlerEndPO = new ArrayList();
+ private final List codeHandlerCatchPO = new ArrayList();
+ private final List codeHandlerClass = new ArrayList();
+ private final List codeFlags = new ArrayList();
+ private int[] code_attr_calls;
+ private final IntList codeLineNumberTableN = new IntList();
+ private final List codeLineNumberTableBciP = new ArrayList();
+ private final IntList codeLineNumberTableLine = new IntList();
+ private final IntList codeLocalVariableTableN = new IntList();
+ private final List codeLocalVariableTableBciP = new ArrayList();
+ private final List codeLocalVariableTableSpanO = new ArrayList();
+ private final List codeLocalVariableTableNameRU = new ArrayList();
+ private final List codeLocalVariableTableTypeRS = new ArrayList();
+ private final IntList codeLocalVariableTableSlot = new IntList();
+ private final IntList codeLocalVariableTypeTableN = new IntList();
+ private final List codeLocalVariableTypeTableBciP = new ArrayList();
+ private final List codeLocalVariableTypeTableSpanO = new ArrayList();
+ private final List codeLocalVariableTypeTableNameRU = new ArrayList();
+ private final List codeLocalVariableTypeTableTypeRS = new ArrayList();
+ private final IntList codeLocalVariableTypeTableSlot = new IntList();
+
+ private final MetadataBandGroup class_RVA_bands;
+ private final MetadataBandGroup class_RIA_bands;
+ private final MetadataBandGroup field_RVA_bands;
+ private final MetadataBandGroup field_RIA_bands;
+ private final MetadataBandGroup method_RVA_bands;
+ private final MetadataBandGroup method_RIA_bands;
+ private final MetadataBandGroup method_RVPA_bands;
+ private final MetadataBandGroup method_RIPA_bands;
+ private final MetadataBandGroup method_AD_bands;
+
+ private final List classAttributeBands = new ArrayList();
+ private final List methodAttributeBands = new ArrayList();
+ private final List fieldAttributeBands = new ArrayList();
+ private final List codeAttributeBands = new ArrayList();
+
+ private final List tempFieldFlags = new ArrayList();
+ private final List tempFieldDesc = new ArrayList();
+ private final List tempMethodFlags = new ArrayList();
+ private final List tempMethodDesc = new ArrayList();
+ private TempParamAnnotation tempMethodRVPA;
+ private TempParamAnnotation tempMethodRIPA;
+
+ private boolean anySyntheticClasses = false;
+ private boolean anySyntheticFields = false;
+ private boolean anySyntheticMethods = false;
+ private final Segment segment;
+
+ private final Map classReferencesInnerClass = new HashMap();
+ private final boolean stripDebug;
+
+ private int index = 0;
+
+ private int numMethodArgs = 0;
+ private int[] class_InnerClasses_N;
+ private CPClass[] class_InnerClasses_RC;
+ private int[] class_InnerClasses_F;
+ private List classInnerClassesOuterRCN;
+ private List classInnerClassesNameRUN;
+
+ public ClassBands(Segment segment, int numClasses, int effort, boolean stripDebug) throws IOException {
+ super(effort, segment.getSegmentHeader());
+ this.stripDebug = stripDebug;
+ this.segment = segment;
+ this.cpBands = segment.getCpBands();
+ this.attrBands = segment.getAttrBands();
+ class_this = new CPClass[numClasses];
+ class_super = new CPClass[numClasses];
+ class_interface_count = new int[numClasses];
+ class_interface = new CPClass[numClasses][];
+ class_field_count = new int[numClasses];
+ class_method_count = new int[numClasses];
+ field_descr = new CPNameAndType[numClasses][];
+ field_flags = new long[numClasses][];
+ method_descr = new CPNameAndType[numClasses][];
+ method_flags = new long[numClasses][];
+ for (int i = 0; i < numClasses; i++) {
+ field_flags[i] = new long[0];
+ method_flags[i] = new long[0];
+ }
+ // minor_versions = new int[numClasses];
+ major_versions = new int[numClasses];
+ class_flags = new long[numClasses];
+
+ class_RVA_bands = new MetadataBandGroup("RVA", MetadataBandGroup.CONTEXT_CLASS, cpBands, segmentHeader, effort);
+ class_RIA_bands = new MetadataBandGroup("RIA", MetadataBandGroup.CONTEXT_CLASS, cpBands, segmentHeader, effort);
+ field_RVA_bands = new MetadataBandGroup("RVA", MetadataBandGroup.CONTEXT_FIELD, cpBands, segmentHeader, effort);
+ field_RIA_bands = new MetadataBandGroup("RIA", MetadataBandGroup.CONTEXT_FIELD, cpBands, segmentHeader, effort);
+ method_RVA_bands = new MetadataBandGroup("RVA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
+ method_RIA_bands = new MetadataBandGroup("RIA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
+ method_RVPA_bands = new MetadataBandGroup("RVPA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
+ method_RIPA_bands = new MetadataBandGroup("RIPA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
+ method_AD_bands = new MetadataBandGroup("AD", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
+
+ createNewAttributeBands();
+ }
+
+ private void createNewAttributeBands() throws IOException {
+ List classAttributeLayouts = attrBands.getClassAttributeLayouts();
+ for (Iterator iterator = classAttributeLayouts.iterator(); iterator.hasNext();) {
+ AttributeDefinition def = (AttributeDefinition) iterator.next();
+ classAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
+ }
+ List methodAttributeLayouts = attrBands.getMethodAttributeLayouts();
+ for (Iterator iterator = methodAttributeLayouts.iterator(); iterator.hasNext();) {
+ AttributeDefinition def = (AttributeDefinition) iterator.next();
+ methodAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
+ }
+ List fieldAttributeLayouts = attrBands.getFieldAttributeLayouts();
+ for (Iterator iterator = fieldAttributeLayouts.iterator(); iterator.hasNext();) {
+ AttributeDefinition def = (AttributeDefinition) iterator.next();
+ fieldAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
+ }
+ List codeAttributeLayouts = attrBands.getCodeAttributeLayouts();
+ for (Iterator iterator = codeAttributeLayouts.iterator(); iterator.hasNext();) {
+ AttributeDefinition def = (AttributeDefinition) iterator.next();
+ codeAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
+ }
+ }
+
+ public void addClass(int major, int flags, String className,
+ String signature, String superName, String[] interfaces) {
+ class_this[index] = cpBands.getCPClass(className);
+ class_super[index] = cpBands.getCPClass(superName);
+ class_interface_count[index] = interfaces.length;
+ class_interface[index] = new CPClass[interfaces.length];
+ for (int i = 0; i < interfaces.length; i++) {
+ class_interface[index][i] = cpBands.getCPClass(interfaces[i]);
+ }
+ major_versions[index] = major;
+ class_flags[index] = flags;
+ if(!anySyntheticClasses && ((flags & (1 << 12)) != 0) && segment.getCurrentClassReader().hasSyntheticAttributes()) {
+ cpBands.addCPUtf8("Synthetic");
+ anySyntheticClasses = true;
+ }
+ if((flags & Opcodes.ACC_DEPRECATED) != 0) { // ASM uses (1<<17) flag for deprecated
+ flags = flags & ~Opcodes.ACC_DEPRECATED;
+ flags = flags | (1<<20);
+ }
+ if(signature != null) {
+ class_flags[index] |= (1 << 19);
+ classSignature.add(cpBands.getCPSignature(signature));
+ }
+ }
+
+ public void currentClassReferencesInnerClass(CPClass inner) {
+ if(!(index >= class_this.length)) {
+ CPClass currentClass = class_this[index];
+ if(currentClass != null && !currentClass.equals(inner) && !isInnerClassOf(currentClass.toString(), inner)) {
+ Set referencedInnerClasses = (Set)classReferencesInnerClass.get(currentClass);
+ if(referencedInnerClasses == null) {
+ referencedInnerClasses = new HashSet();
+ classReferencesInnerClass.put(currentClass, referencedInnerClasses);
+ }
+ referencedInnerClasses.add(inner);
+ }
+ }
+ }
+
+ private boolean isInnerClassOf(String possibleInner, CPClass possibleOuter) {
+ if(isInnerClass(possibleInner)) {
+ String superClassName = possibleInner.substring(0, possibleInner.lastIndexOf('$'));
+ if(superClassName.equals(possibleOuter.toString())) {
+ return true;
+ } else { // do this recursively
+ return isInnerClassOf(superClassName, possibleOuter);
+ }
+ }
+ return false;
+ }
+
+ private boolean isInnerClass(String possibleInner) {
+ return possibleInner.indexOf('$') != -1;
+ }
+
+ public void addField(int flags, String name, String desc, String signature,
+ Object value) {
+ flags = flags & 0xFFFF;
+ tempFieldDesc.add(cpBands.getCPNameAndType(name, desc));
+ if (signature != null) {
+ fieldSignature.add(cpBands.getCPSignature(signature));
+ flags |= (1 << 19);
+ }
+ if((flags & Opcodes.ACC_DEPRECATED) != 0) { // ASM uses (1<<17) flag for deprecated
+ flags = flags & ~Opcodes.ACC_DEPRECATED;
+ flags = flags | (1<<20);
+ }
+ if (value != null) {
+ fieldConstantValueKQ.add(cpBands.getConstant(value));
+ flags |= (1 << 17);
+ }
+ if(!anySyntheticFields && ((flags & (1 << 12)) != 0) && segment.getCurrentClassReader().hasSyntheticAttributes()) {
+ cpBands.addCPUtf8("Synthetic");
+ anySyntheticFields = true;
+ }
+ tempFieldFlags.add(new Long(flags));
+ }
+
+ /**
+ * All input classes for the segment have now been read in, so this method
+ * is called so that this class can calculate/complete anything it could not
+ * do while classes were being read.
+ */
+ public void finaliseBands() {
+ int defaultMajorVersion = segmentHeader.getDefaultMajorVersion();
+ for (int i = 0; i < class_flags.length; i++) {
+ int major = major_versions[i];
+ if (major != defaultMajorVersion) {
+ class_flags[i] |= 1 << 24;
+ classFileVersionMajor.add(major);
+ classFileVersionMinor.add(0);
+ }
+ }
+ // Calculate code headers
+ codeHeaders = new int[codeHandlerCount.size()];
+ int removed = 0;
+ for (int i = 0; i < codeHeaders.length; i++) {
+ int numHandlers = codeHandlerCount.get(i - removed);
+ int maxLocals = codeMaxLocals.get(i - removed);
+ int maxStack = codeMaxStack.get(i - removed);
+ if (numHandlers == 0) {
+ int header = maxLocals * 12 + maxStack + 1;
+ if (header < 145 && maxStack < 12) {
+ codeHeaders[i] = header;
+ }
+ } else if (numHandlers == 1) {
+ int header = maxLocals * 8 + maxStack + 145;
+ if (header < 209 && maxStack < 8) {
+ codeHeaders[i] = header;
+ }
+ } else if (numHandlers == 2) {
+ int header = maxLocals * 7 + maxStack + 209;
+ if (header < 256 && maxStack < 7) {
+ codeHeaders[i] = header;
+ }
+ }
+ if (codeHeaders[i] != 0) { // Remove the redundant values from
+ // codeHandlerCount, codeMaxLocals and
+ // codeMaxStack
+ codeHandlerCount.remove(i - removed);
+ codeMaxLocals.remove(i - removed);
+ codeMaxStack.remove(i - removed);
+ removed++;
+ } else if (!segment.getSegmentHeader().have_all_code_flags()) {
+ codeFlags.add(new Long(0));
+ }
+ }
+
+ // Compute any required IcLocals
+ IntList innerClassesN = new IntList();
+ List icLocal = new ArrayList();
+ for (int i = 0; i < class_this.length; i++) {
+ CPClass cpClass = class_this[i];
+ Set referencedInnerClasses = (Set) classReferencesInnerClass.get(cpClass);
+ if(referencedInnerClasses != null) {
+ int innerN = 0;
+ List innerClasses = segment.getIcBands().getInnerClassesForOuter(cpClass.toString());
+ if(innerClasses != null) {
+ for (Iterator iterator2 = innerClasses.iterator(); iterator2
+ .hasNext();) {
+ referencedInnerClasses.remove(((IcTuple)iterator2.next()).C);
+ }
+ }
+ for (Iterator iterator2 = referencedInnerClasses.iterator(); iterator2
+ .hasNext();) {
+ CPClass inner = (CPClass) iterator2.next();
+ IcTuple icTuple = segment.getIcBands().getIcTuple(inner);
+ if(icTuple != null && ! icTuple.isAnonymous()) {
+ // should transmit an icLocal entry
+ icLocal.add(icTuple);
+ innerN++;
+ }
+ }
+ if(innerN != 0) {
+ innerClassesN.add(innerN);
+ class_flags[i] |= (1 << 23);
+ }
+ }
+ }
+ class_InnerClasses_N = innerClassesN.toArray();
+ class_InnerClasses_RC = new CPClass[icLocal.size()];
+ class_InnerClasses_F = new int[icLocal.size()];
+ classInnerClassesOuterRCN = new ArrayList();
+ classInnerClassesNameRUN = new ArrayList();
+ for (int i = 0; i < class_InnerClasses_RC.length; i++) {
+ IcTuple icTuple = (IcTuple) icLocal.get(i);
+ class_InnerClasses_RC[i] = (icTuple.C);
+ if(icTuple.C2 == null && icTuple.N == null) {
+ class_InnerClasses_F[i] = 0;
+ } else {
+ if (icTuple.F == 0) {
+ class_InnerClasses_F[i] = 0x00010000;
+ } else {
+ class_InnerClasses_F[i] = icTuple.F;
+ }
+ classInnerClassesOuterRCN.add(icTuple.C2);
+ classInnerClassesNameRUN.add(icTuple.N);
+ }
+ }
+ // Calculate any backwards calls from metadata bands
+ IntList classAttrCalls = new IntList();
+ IntList fieldAttrCalls = new IntList();
+ IntList methodAttrCalls = new IntList();
+ IntList codeAttrCalls = new IntList();
+
+ if(class_RVA_bands.hasContent()) {
+ classAttrCalls.add(class_RVA_bands.numBackwardsCalls());
+ }
+ if(class_RIA_bands.hasContent()) {
+ classAttrCalls.add(class_RIA_bands.numBackwardsCalls());
+ }
+ if(field_RVA_bands.hasContent()) {
+ fieldAttrCalls.add(field_RVA_bands.numBackwardsCalls());
+ }
+ if(field_RIA_bands.hasContent()) {
+ fieldAttrCalls.add(field_RIA_bands.numBackwardsCalls());
+ }
+ if(method_RVA_bands.hasContent()) {
+ methodAttrCalls.add(method_RVA_bands.numBackwardsCalls());
+ }
+ if(method_RIA_bands.hasContent()) {
+ methodAttrCalls.add(method_RIA_bands.numBackwardsCalls());
+ }
+ if(method_RVPA_bands.hasContent()) {
+ methodAttrCalls.add(method_RVPA_bands.numBackwardsCalls());
+ }
+ if(method_RIPA_bands.hasContent()) {
+ methodAttrCalls.add(method_RIPA_bands.numBackwardsCalls());
+ }
+ if(method_AD_bands.hasContent()) {
+ methodAttrCalls.add(method_AD_bands.numBackwardsCalls());
+ }
+
+ // Sort non-predefined attribute bands
+ Comparator comparator = new Comparator() {
+ public int compare(Object arg0, Object arg1) {
+ NewAttributeBands bands0 = (NewAttributeBands)arg0;
+ NewAttributeBands bands1 = (NewAttributeBands)arg1;
+ return bands0.getFlagIndex() - bands1.getFlagIndex();
+ }
+ };
+ Collections.sort(classAttributeBands, comparator);
+ Collections.sort(methodAttributeBands, comparator);
+ Collections.sort(fieldAttributeBands, comparator);
+ Collections.sort(codeAttributeBands, comparator);
+
+ for (Iterator iterator = classAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands bands = (NewAttributeBands) iterator.next();
+ if(bands.isUsedAtLeastOnce()) {
+ int[] backwardsCallCounts = bands.numBackwardsCalls();
+ for (int i = 0; i < backwardsCallCounts.length; i++) {
+ classAttrCalls.add(backwardsCallCounts[i]);
+ }
+ }
+ }
+ for (Iterator iterator = methodAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands bands = (NewAttributeBands) iterator.next();
+ if(bands.isUsedAtLeastOnce()) {
+ int[] backwardsCallCounts = bands.numBackwardsCalls();
+ for (int i = 0; i < backwardsCallCounts.length; i++) {
+ methodAttrCalls.add(backwardsCallCounts[i]);
+ }
+ }
+ }
+ for (Iterator iterator = fieldAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands bands = (NewAttributeBands) iterator.next();
+ if(bands.isUsedAtLeastOnce()) {
+ int[] backwardsCallCounts = bands.numBackwardsCalls();
+ for (int i = 0; i < backwardsCallCounts.length; i++) {
+ fieldAttrCalls.add(backwardsCallCounts[i]);
+ }
+ }
+ }
+ for (Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands bands = (NewAttributeBands) iterator.next();
+ if(bands.isUsedAtLeastOnce()) {
+ int[] backwardsCallCounts = bands.numBackwardsCalls();
+ for (int i = 0; i < backwardsCallCounts.length; i++) {
+ codeAttrCalls.add(backwardsCallCounts[i]);
+ }
+ }
+ }
+
+ class_attr_calls = classAttrCalls.toArray();
+ field_attr_calls = fieldAttrCalls.toArray();
+ method_attr_calls = methodAttrCalls.toArray();
+ code_attr_calls = codeAttrCalls.toArray();
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ PackingUtils.log("Writing class bands...");
+
+ byte[] encodedBand = encodeBandInt("class_this", getInts(class_this),
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_this[" + class_this.length + "]");
+
+ encodedBand = encodeBandInt("class_super", getInts(class_super),
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_super[" + class_super.length + "]");
+
+ encodedBand = encodeBandInt("class_interface_count",
+ class_interface_count, Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_interface_count["
+ + class_interface_count.length + "]");
+
+ int totalInterfaces = sum(class_interface_count);
+ int[] classInterface = new int[totalInterfaces];
+ int k = 0;
+ for (int i = 0; i < class_interface.length; i++) {
+ if (class_interface[i] != null) {
+ for (int j = 0; j < class_interface[i].length; j++) {
+ CPClass cpClass = class_interface[i][j];
+ classInterface[k] = cpClass.getIndex();
+ k++;
+ }
+ }
+ }
+
+ encodedBand = encodeBandInt("class_interface", classInterface,
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_interface[" + classInterface.length + "]");
+
+ encodedBand = encodeBandInt("class_field_count", class_field_count,
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_field_count[" + class_field_count.length
+ + "]");
+
+ encodedBand = encodeBandInt("class_method_count", class_method_count,
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_method_count[" + class_method_count.length
+ + "]");
+
+ int totalFields = sum(class_field_count);
+ int[] fieldDescr = new int[totalFields];
+ k = 0;
+ for (int i = 0; i < index; i++) {
+ for (int j = 0; j < field_descr[i].length; j++) {
+ CPNameAndType descr = field_descr[i][j];
+ fieldDescr[k] = descr.getIndex();
+ k++;
+ }
+ }
+
+ encodedBand = encodeBandInt("field_descr", fieldDescr, Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from field_descr[" + fieldDescr.length + "]");
+
+ writeFieldAttributeBands(out);
+
+ int totalMethods = sum(class_method_count);
+ int[] methodDescr = new int[totalMethods];
+ k = 0;
+ for (int i = 0; i < index; i++) {
+ for (int j = 0; j < method_descr[i].length; j++) {
+ CPNameAndType descr = method_descr[i][j];
+ methodDescr[k] = descr.getIndex();
+ k++;
+ }
+ }
+
+ encodedBand = encodeBandInt("method_descr", methodDescr, Codec.MDELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from method_descr[" + methodDescr.length + "]");
+
+ writeMethodAttributeBands(out);
+ writeClassAttributeBands(out);
+ writeCodeBands(out);
+ }
+
+ private int sum(int[] ints) {
+ int sum = 0;
+ for (int i = 0; i < ints.length; i++) {
+ sum += ints[i];
+ }
+ return sum;
+ }
+
+ private void writeFieldAttributeBands(OutputStream out) throws IOException,
+ Pack200Exception {
+ byte[] encodedBand = encodeFlags("field_flags", field_flags,
+ Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader
+ .have_field_flags_hi());
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from field_flags[" + field_flags.length + "]");
+
+ // *field_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
+ // *field_attr_indexes :UNSIGNED5 [SUM(*field_attr_count)]
+ encodedBand = encodeBandInt("field_attr_calls", field_attr_calls,
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from field_attr_calls[" + field_attr_calls.length
+ + "]");
+
+ encodedBand = encodeBandInt("fieldConstantValueKQ",
+ cpEntryListToArray(fieldConstantValueKQ), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from fieldConstantValueKQ["
+ + fieldConstantValueKQ.size() + "]");
+
+ encodedBand = encodeBandInt("fieldSignature",
+ cpEntryListToArray(fieldSignature), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from fieldSignature[" + fieldSignature.size() + "]");
+
+ field_RVA_bands.pack(out);
+ field_RIA_bands.pack(out);
+ for (Iterator iterator = fieldAttributeBands.iterator(); iterator
+ .hasNext();) {
+ NewAttributeBands bands = (NewAttributeBands) iterator.next();
+ bands.pack(out);
+ }
+ }
+
+ private void writeMethodAttributeBands(OutputStream out)
+ throws IOException, Pack200Exception {
+ byte[] encodedBand = encodeFlags("method_flags", method_flags,
+ Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader
+ .have_method_flags_hi());
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from method_flags[" + method_flags.length + "]");
+
+ // *method_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
+ // *method_attr_indexes :UNSIGNED5 [SUM(*method_attr_count)]
+ encodedBand = encodeBandInt("method_attr_calls", method_attr_calls,
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from method_attr_calls[" + method_attr_calls.length
+ + "]");
+
+ encodedBand = encodeBandInt("methodExceptionNumber",
+ methodExceptionNumber.toArray(), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from methodExceptionNumber["
+ + methodExceptionNumber.size() + "]");
+
+ encodedBand = encodeBandInt("methodExceptionClasses",
+ cpEntryListToArray(methodExceptionClasses), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from methodExceptionClasses["
+ + methodExceptionClasses.size() + "]");
+
+ encodedBand = encodeBandInt("methodSignature",
+ cpEntryListToArray(methodSignature), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils
+ .log("Wrote " + encodedBand.length
+ + " bytes from methodSignature["
+ + methodSignature.size() + "]");
+
+ method_RVA_bands.pack(out);
+ method_RIA_bands.pack(out);
+ method_RVPA_bands.pack(out);
+ method_RIPA_bands.pack(out);
+ method_AD_bands.pack(out);
+ for (Iterator iterator = methodAttributeBands.iterator(); iterator
+ .hasNext();) {
+ NewAttributeBands bands = (NewAttributeBands) iterator.next();
+ bands.pack(out);
+ }
+ }
+
+ private void writeClassAttributeBands(OutputStream out) throws IOException,
+ Pack200Exception {
+ byte[] encodedBand = encodeFlags("class_flags", class_flags,
+ Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader
+ .have_class_flags_hi());
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_flags[" + class_flags.length + "]");
+
+ // These bands are not needed, but could be used to reduce the size of
+ // the archive if there are enough different non-standard attributes
+ // defined that segmentHeader.have_class_flags_hi() is true. The same
+ // applies to method_attr_count, field_attr_count, code_attr_count etc.
+
+ // *class_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
+ // *class_attr_indexes :UNSIGNED5 [SUM(*class_attr_count)]
+
+ encodedBand = encodeBandInt("class_attr_calls", class_attr_calls,
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_attr_calls[" + class_attr_calls.length
+ + "]");
+
+ encodedBand = encodeBandInt("classSourceFile",
+ cpEntryOrNullListToArray(classSourceFile), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils
+ .log("Wrote " + encodedBand.length
+ + " bytes from classSourceFile["
+ + classSourceFile.size() + "]");
+
+ encodedBand = encodeBandInt("class_enclosing_method_RC",
+ cpEntryListToArray(classEnclosingMethodClass), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_enclosing_method_RC["
+ + classEnclosingMethodClass.size() + "]");
+
+ encodedBand = encodeBandInt("class_EnclosingMethod_RDN",
+ cpEntryOrNullListToArray(classEnclosingMethodDesc),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_EnclosingMethod_RDN["
+ + classEnclosingMethodDesc.size() + "]");
+
+ encodedBand = encodeBandInt("class_Signature_RS",
+ cpEntryListToArray(classSignature), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_Signature_RS[" + classSignature.size()
+ + "]");
+
+ class_RVA_bands.pack(out);
+ class_RIA_bands.pack(out);
+
+ encodedBand = encodeBandInt("class_InnerClasses_N",
+ class_InnerClasses_N, Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_InnerClasses_N["
+ + class_InnerClasses_N.length + "]");
+
+ encodedBand = encodeBandInt("class_InnerClasses_RC",
+ getInts(class_InnerClasses_RC), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_InnerClasses_RC["
+ + class_InnerClasses_RC.length + "]");
+
+ encodedBand = encodeBandInt("class_InnerClasses_F",
+ class_InnerClasses_F, Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_InnerClasses_F["
+ + class_InnerClasses_F.length + "]");
+
+ encodedBand = encodeBandInt("class_InnerClasses_outer_RCN",
+ cpEntryOrNullListToArray(classInnerClassesOuterRCN),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_InnerClasses_outer_RCN["
+ + classInnerClassesOuterRCN.size() + "]");
+
+ encodedBand = encodeBandInt("class_InnerClasses_name_RUN",
+ cpEntryOrNullListToArray(classInnerClassesNameRUN),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from class_InnerClasses_name_RUN["
+ + classInnerClassesNameRUN.size() + "]");
+
+ encodedBand = encodeBandInt("classFileVersionMinor",
+ classFileVersionMinor.toArray(), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from classFileVersionMinor["
+ + classFileVersionMinor.size() + "]");
+
+ encodedBand = encodeBandInt("classFileVersionMajor",
+ classFileVersionMajor.toArray(), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from classFileVersionMajor["
+ + classFileVersionMajor.size() + "]");
+
+ for (Iterator iterator = classAttributeBands.iterator(); iterator
+ .hasNext();) {
+ NewAttributeBands bands = (NewAttributeBands) iterator.next();
+ bands.pack(out);
+ }
+ }
+
+ private int[] getInts(CPClass[] cpClasses) {
+ int[] ints = new int[cpClasses.length];
+ for (int i = 0; i < ints.length; i++) {
+ if(cpClasses[i] != null) {
+ ints[i] = cpClasses[i].getIndex();
+ }
+ }
+ return ints;
+ }
+
+ private void writeCodeBands(OutputStream out) throws IOException,
+ Pack200Exception {
+ byte[] encodedBand = encodeBandInt("codeHeaders", codeHeaders,
+ Codec.BYTE1);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from codeHeaders[" + codeHeaders.length + "]");
+
+ encodedBand = encodeBandInt("codeMaxStack", codeMaxStack.toArray(),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from codeMaxStack[" + codeMaxStack.size() + "]");
+
+ encodedBand = encodeBandInt("codeMaxLocals", codeMaxLocals.toArray(),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from codeMaxLocals[" + codeMaxLocals.size() + "]");
+
+ encodedBand = encodeBandInt("codeHandlerCount", codeHandlerCount
+ .toArray(), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from codeHandlerCount[" + codeHandlerCount.size()
+ + "]");
+
+ encodedBand = encodeBandInt("codeHandlerStartP",
+ integerListToArray(codeHandlerStartP), Codec.BCI5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from codeHandlerStartP[" + codeHandlerStartP.size()
+ + "]");
+
+ encodedBand = encodeBandInt("codeHandlerEndPO",
+ integerListToArray(codeHandlerEndPO), Codec.BRANCH5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from codeHandlerEndPO[" + codeHandlerEndPO.size()
+ + "]");
+
+ encodedBand = encodeBandInt("codeHandlerCatchPO",
+ integerListToArray(codeHandlerCatchPO), Codec.BRANCH5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from codeHandlerCatchPO[" + codeHandlerCatchPO.size()
+ + "]");
+
+ encodedBand = encodeBandInt("codeHandlerClass",
+ cpEntryOrNullListToArray(codeHandlerClass), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from codeHandlerClass[" + codeHandlerClass.size()
+ + "]");
+
+ writeCodeAttributeBands(out);
+ }
+
+ private void writeCodeAttributeBands(OutputStream out) throws IOException,
+ Pack200Exception {
+ byte[] encodedBand = encodeFlags("codeFlags",
+ longListToArray(codeFlags), Codec.UNSIGNED5, Codec.UNSIGNED5,
+ segmentHeader.have_code_flags_hi());
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from codeFlags[" + codeFlags.size() + "]");
+
+ // *code_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
+ // *code_attr_indexes :UNSIGNED5 [SUM(*code_attr_count)]
+ encodedBand = encodeBandInt("code_attr_calls", code_attr_calls,
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils
+ .log("Wrote " + encodedBand.length
+ + " bytes from code_attr_calls["
+ + code_attr_calls.length + "]");
+
+ encodedBand = encodeBandInt("code_LineNumberTable_N",
+ codeLineNumberTableN.toArray(), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LineNumberTable_N["
+ + codeLineNumberTableN.size() + "]");
+
+ encodedBand = encodeBandInt("code_LineNumberTable_bci_P",
+ integerListToArray(codeLineNumberTableBciP), Codec.BCI5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LineNumberTable_bci_P["
+ + codeLineNumberTableBciP.size() + "]");
+
+ encodedBand = encodeBandInt("code_LineNumberTable_line",
+ codeLineNumberTableLine.toArray(), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LineNumberTable_line["
+ + codeLineNumberTableLine.size() + "]");
+
+ encodedBand = encodeBandInt("code_LocalVariableTable_N",
+ codeLocalVariableTableN.toArray(), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LocalVariableTable_N["
+ + codeLocalVariableTableN.size() + "]");
+
+ encodedBand = encodeBandInt("code_LocalVariableTable_bci_P",
+ integerListToArray(codeLocalVariableTableBciP), Codec.BCI5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LocalVariableTable_bci_P["
+ + codeLocalVariableTableBciP.size() + "]");
+
+ encodedBand = encodeBandInt("code_LocalVariableTable_span_O",
+ integerListToArray(codeLocalVariableTableSpanO), Codec.BRANCH5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LocalVariableTable_span_O["
+ + codeLocalVariableTableSpanO.size() + "]");
+
+ encodedBand = encodeBandInt("code_LocalVariableTable_name_RU",
+ cpEntryListToArray(codeLocalVariableTableNameRU),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LocalVariableTable_name_RU["
+ + codeLocalVariableTableNameRU.size() + "]");
+
+ encodedBand = encodeBandInt("code_LocalVariableTable_type_RS",
+ cpEntryListToArray(codeLocalVariableTableTypeRS),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LocalVariableTable_type_RS["
+ + codeLocalVariableTableTypeRS.size() + "]");
+
+ encodedBand = encodeBandInt("code_LocalVariableTable_slot",
+ codeLocalVariableTableSlot.toArray(), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LocalVariableTable_slot["
+ + codeLocalVariableTableSlot.size() + "]");
+
+ encodedBand = encodeBandInt("code_LocalVariableTypeTable_N",
+ codeLocalVariableTypeTableN.toArray(), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LocalVariableTypeTable_N["
+ + codeLocalVariableTypeTableN.size() + "]");
+
+ encodedBand = encodeBandInt("code_LocalVariableTypeTable_bci_P",
+ integerListToArray(codeLocalVariableTypeTableBciP), Codec.BCI5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LocalVariableTypeTable_bci_P["
+ + codeLocalVariableTypeTableBciP.size() + "]");
+
+ encodedBand = encodeBandInt("code_LocalVariableTypeTable_span_O",
+ integerListToArray(codeLocalVariableTypeTableSpanO), Codec.BRANCH5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LocalVariableTypeTable_span_O["
+ + codeLocalVariableTypeTableSpanO.size() + "]");
+
+ encodedBand = encodeBandInt("code_LocalVariableTypeTable_name_RU",
+ cpEntryListToArray(codeLocalVariableTypeTableNameRU),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LocalVariableTypeTable_name_RU["
+ + codeLocalVariableTypeTableNameRU.size() + "]");
+
+ encodedBand = encodeBandInt("code_LocalVariableTypeTable_type_RS",
+ cpEntryListToArray(codeLocalVariableTypeTableTypeRS),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LocalVariableTypeTable_type_RS["
+ + codeLocalVariableTypeTableTypeRS.size() + "]");
+
+ encodedBand = encodeBandInt("code_LocalVariableTypeTable_slot",
+ codeLocalVariableTypeTableSlot.toArray(), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from code_LocalVariableTypeTable_slot["
+ + codeLocalVariableTypeTableSlot.size() + "]");
+
+ for (Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands bands = (NewAttributeBands) iterator.next();
+ bands.pack(out);
+ }
+ }
+
+ public void addMethod(int flags, String name, String desc,
+ String signature, String[] exceptions) {
+ CPNameAndType nt = cpBands.getCPNameAndType(name, desc);
+ tempMethodDesc.add(nt);
+ if (signature != null) {
+ methodSignature.add(cpBands.getCPSignature(signature));
+ flags |= (1 << 19);
+ }
+ if (exceptions != null) {
+ methodExceptionNumber.add(exceptions.length);
+ for (int i = 0; i < exceptions.length; i++) {
+ methodExceptionClasses.add(cpBands.getCPClass(exceptions[i]));
+ }
+ flags |= (1 << 18);
+ }
+ if((flags & Opcodes.ACC_DEPRECATED) != 0) { // ASM uses (1<<17) flag for deprecated
+ flags = flags & ~Opcodes.ACC_DEPRECATED;
+ flags = flags | (1<<20);
+ }
+ tempMethodFlags.add(new Long(flags));
+ numMethodArgs = countArgs(desc);
+ if(!anySyntheticMethods && ((flags & (1 << 12)) != 0) && segment.getCurrentClassReader().hasSyntheticAttributes()) {
+ cpBands.addCPUtf8("Synthetic");
+ anySyntheticMethods = true;
+ }
+ }
+
+ public void endOfMethod() {
+ if (tempMethodRVPA != null) {
+ method_RVPA_bands.addParameterAnnotation(tempMethodRVPA.numParams,
+ tempMethodRVPA.annoN, tempMethodRVPA.pairN,
+ tempMethodRVPA.typeRS, tempMethodRVPA.nameRU,
+ tempMethodRVPA.t, tempMethodRVPA.values,
+ tempMethodRVPA.caseArrayN, tempMethodRVPA.nestTypeRS,
+ tempMethodRVPA.nestNameRU, tempMethodRVPA.nestPairN);
+ tempMethodRVPA = null;
+ }
+ if (tempMethodRIPA != null) {
+ method_RIPA_bands.addParameterAnnotation(tempMethodRIPA.numParams,
+ tempMethodRIPA.annoN, tempMethodRIPA.pairN,
+ tempMethodRIPA.typeRS, tempMethodRIPA.nameRU,
+ tempMethodRIPA.t, tempMethodRIPA.values,
+ tempMethodRIPA.caseArrayN, tempMethodRIPA.nestTypeRS,
+ tempMethodRIPA.nestNameRU, tempMethodRIPA.nestPairN);
+ tempMethodRIPA = null;
+ }
+ if(codeFlags.size() > 0) {
+ long latestCodeFlag = ((Long)codeFlags.get(codeFlags.size() - 1)).longValue();
+ int latestLocalVariableTableN = codeLocalVariableTableN.get(codeLocalVariableTableN.size() - 1);
+ if(latestCodeFlag == (1 << 2) && latestLocalVariableTableN == 0) {
+ codeLocalVariableTableN.remove(codeLocalVariableTableN.size() - 1);
+ codeFlags.remove(codeFlags.size() - 1);
+ codeFlags.add(new Integer(0));
+ }
+ }
+ }
+
+ protected static int countArgs(String descriptor) {
+ int bra = descriptor.indexOf('(');
+ int ket = descriptor.indexOf(')');
+ if (bra == -1 || ket == -1 || ket < bra)
+ throw new IllegalArgumentException("No arguments");
+
+ boolean inType = false;
+ boolean consumingNextType = false;
+ int count = 0;
+ for (int i = bra + 1; i < ket; i++) {
+ char charAt = descriptor.charAt(i);
+ if (inType && charAt == ';') {
+ inType = false;
+ consumingNextType = false;
+ } else if (!inType && charAt == 'L') {
+ inType = true;
+ count++;
+ } else if (charAt == '[') {
+ consumingNextType = true;
+ } else if (inType) {
+ // NOP
+ } else {
+ if (consumingNextType) {
+ count++;
+ consumingNextType = false;
+ } else {
+ if (charAt == 'D' || charAt == 'J') {
+ count += 2;
+ } else {
+ count++;
+ }
+ }
+ }
+ }
+ return count;
+ }
+
+ public void endOfClass() { // All the data for the current class has been
+ // read
+ int numFields = tempFieldDesc.size();
+ class_field_count[index] = numFields;
+ field_descr[index] = new CPNameAndType[numFields];
+ field_flags[index] = new long[numFields];
+ for (int i = 0; i < numFields; i++) {
+ field_descr[index][i] = (CPNameAndType) tempFieldDesc.get(i);
+ field_flags[index][i] = ((Long) tempFieldFlags.get(i)).longValue();
+ }
+ int numMethods = tempMethodDesc.size();
+ class_method_count[index] = numMethods;
+ method_descr[index] = new CPNameAndType[numMethods];
+ method_flags[index] = new long[numMethods];
+ for (int i = 0; i < numMethods; i++) {
+ method_descr[index][i] = (CPNameAndType) tempMethodDesc.get(i);
+ method_flags[index][i] = ((Long) tempMethodFlags.get(i))
+ .longValue();
+ }
+ tempFieldDesc.clear();
+ tempFieldFlags.clear();
+ tempMethodDesc.clear();
+ tempMethodFlags.clear();
+ index++;
+ }
+
+ public void addSourceFile(String source) {
+ String implicitSourceFileName = class_this[index].toString();
+ if(implicitSourceFileName.indexOf('$') != -1) {
+ implicitSourceFileName = implicitSourceFileName.substring(0, implicitSourceFileName.indexOf('$'));
+ }
+ implicitSourceFileName = implicitSourceFileName
+ .substring(implicitSourceFileName.lastIndexOf('/') + 1)
+ + ".java";
+ if (source.equals(implicitSourceFileName)) {
+ classSourceFile.add(null);
+ } else {
+ classSourceFile.add(cpBands.getCPUtf8(source));
+ }
+ class_flags[index] |= (1 << 17);
+ }
+
+ public void addEnclosingMethod(String owner, String name, String desc) {
+ class_flags[index] |= (1 << 18);
+ classEnclosingMethodClass.add(cpBands.getCPClass(owner));
+ classEnclosingMethodDesc.add(name == null ? null : cpBands
+ .getCPNameAndType(name, desc));
+ }
+
+ public void addClassAttribute(NewAttribute attribute) {
+ // TODO: backwards calls
+ String attributeName = attribute.type;
+ for (Iterator iterator = classAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands bands = (NewAttributeBands) iterator.next();
+ if(bands.getAttributeName().equals(attributeName)) {
+ bands.addAttribute(attribute);
+ int flagIndex = bands.getFlagIndex();
+ class_flags[index] |= (1 << flagIndex);
+ return;
+ }
+ }
+ throw new RuntimeException("No suitable definition for " + attributeName);
+ }
+
+ public void addFieldAttribute(NewAttribute attribute) {
+ String attributeName = attribute.type;
+ for (Iterator iterator = fieldAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands bands = (NewAttributeBands) iterator.next();
+ if(bands.getAttributeName().equals(attributeName)) {
+ bands.addAttribute(attribute);
+ int flagIndex = bands.getFlagIndex();
+ Long flags = (Long)tempFieldFlags.remove(tempFieldFlags.size() - 1);
+ tempFieldFlags.add(new Long(flags.longValue() | (1 << flagIndex)));
+ return;
+ }
+ }
+ throw new RuntimeException("No suitable definition for " + attributeName);
+ }
+
+ public void addMethodAttribute(NewAttribute attribute) {
+ String attributeName = attribute.type;
+ for (Iterator iterator = methodAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands bands = (NewAttributeBands) iterator.next();
+ if(bands.getAttributeName().equals(attributeName)) {
+ bands.addAttribute(attribute);
+ int flagIndex = bands.getFlagIndex();
+ Long flags = (Long)tempMethodFlags.remove(tempMethodFlags.size() - 1);
+ tempMethodFlags.add(new Long(flags.longValue() | (1 << flagIndex)));
+ return;
+ }
+ }
+ throw new RuntimeException("No suitable definition for " + attributeName);
+ }
+
+ public void addCodeAttribute(NewAttribute attribute) {
+ String attributeName = attribute.type;
+ for (Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands bands = (NewAttributeBands) iterator.next();
+ if(bands.getAttributeName().equals(attributeName)) {
+ bands.addAttribute(attribute);
+ int flagIndex = bands.getFlagIndex();
+ Long flags = (Long)codeFlags.remove(codeFlags.size() - 1);
+ codeFlags.add(new Long(flags.longValue() | (1 << flagIndex)));
+ return;
+ }
+ }
+ throw new RuntimeException("No suitable definition for " + attributeName);
+ }
+
+ public void addMaxStack(int maxStack, int maxLocals) {
+ Long latestFlag = (Long) tempMethodFlags
+ .remove(tempMethodFlags.size() - 1);
+ Long newFlag = new Long(latestFlag.intValue() | (1 << 17));
+ tempMethodFlags.add(newFlag);
+ codeMaxStack.add(maxStack);
+ if ((newFlag.longValue() & (1 << 3)) == 0) { // not static
+ maxLocals--; // minus 'this' local
+ }
+ maxLocals -= numMethodArgs;
+ codeMaxLocals.add(maxLocals);
+ }
+
+ public void addCode() {
+ codeHandlerCount.add(0);
+ if(!stripDebug) {
+ codeFlags.add(new Long((1 << 2)));
+ codeLocalVariableTableN.add(0);
+ }
+ }
+
+ public void addHandler(Label start, Label end, Label handler, String type) {
+ int handlers = codeHandlerCount.remove(codeHandlerCount
+ .size() - 1);
+ codeHandlerCount.add(handlers + 1);
+ codeHandlerStartP.add(start);
+ codeHandlerEndPO.add(end);
+ codeHandlerCatchPO.add(handler);
+ codeHandlerClass.add(type == null ? null : cpBands.getCPClass(type));
+ }
+
+ public void addLineNumber(int line, Label start) {
+ Long latestCodeFlag = (Long) codeFlags.get(codeFlags.size() - 1);
+ if ((latestCodeFlag.intValue() & (1 << 1)) == 0) {
+ codeFlags.remove(codeFlags.size() - 1);
+ codeFlags.add(new Long(latestCodeFlag.intValue() | (1 << 1)));
+ codeLineNumberTableN.add(1);
+ } else {
+ codeLineNumberTableN
+ .increment(codeLineNumberTableN.size() - 1);
+ }
+ codeLineNumberTableLine.add(line);
+ codeLineNumberTableBciP.add(start);
+ }
+
+ public void addLocalVariable(String name, String desc, String signature,
+ Label start, Label end, int indx) {
+ if (signature != null) { // LocalVariableTypeTable attribute
+ Long latestCodeFlag = (Long) codeFlags.get(codeFlags.size() - 1);
+ if ((latestCodeFlag.intValue() & (1 << 3)) == 0) {
+ codeFlags.remove(codeFlags.size() - 1);
+ codeFlags.add(new Long(latestCodeFlag.intValue() | (1 << 3)));
+ codeLocalVariableTypeTableN.add(1);
+ } else {
+ codeLocalVariableTypeTableN
+ .increment(codeLocalVariableTypeTableN.size() - 1);
+ }
+ codeLocalVariableTypeTableBciP.add(start);
+ codeLocalVariableTypeTableSpanO.add(end);
+ codeLocalVariableTypeTableNameRU.add(cpBands.getCPUtf8(name));
+ codeLocalVariableTypeTableTypeRS.add(cpBands
+ .getCPSignature(signature));
+ codeLocalVariableTypeTableSlot.add(indx);
+ }
+ // LocalVariableTable attribute
+ codeLocalVariableTableN
+ .increment(codeLocalVariableTableN.size() - 1);
+ codeLocalVariableTableBciP.add(start);
+ codeLocalVariableTableSpanO.add(end);
+ codeLocalVariableTableNameRU.add(cpBands.getCPUtf8(name));
+ codeLocalVariableTableTypeRS.add(cpBands.getCPSignature(desc));
+ codeLocalVariableTableSlot.add(indx);
+ }
+
+ public void doBciRenumbering(IntList bciRenumbering, Map labelsToOffsets) {
+ renumberBci(codeLineNumberTableBciP, bciRenumbering, labelsToOffsets);
+ renumberBci(codeLocalVariableTableBciP, bciRenumbering, labelsToOffsets);
+ renumberOffsetBci(codeLocalVariableTableBciP,
+ codeLocalVariableTableSpanO, bciRenumbering, labelsToOffsets);
+ renumberBci(codeLocalVariableTypeTableBciP, bciRenumbering,
+ labelsToOffsets);
+ renumberOffsetBci(codeLocalVariableTypeTableBciP,
+ codeLocalVariableTypeTableSpanO, bciRenumbering,
+ labelsToOffsets);
+ renumberBci(codeHandlerStartP, bciRenumbering, labelsToOffsets);
+ renumberOffsetBci(codeHandlerStartP, codeHandlerEndPO,
+ bciRenumbering, labelsToOffsets);
+ renumberDoubleOffsetBci(codeHandlerStartP, codeHandlerEndPO, codeHandlerCatchPO,
+ bciRenumbering, labelsToOffsets);
+
+ for (Iterator iterator = classAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands newAttributeBandSet = (NewAttributeBands) iterator.next();
+ newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
+ }
+ for (Iterator iterator = methodAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands newAttributeBandSet = (NewAttributeBands) iterator.next();
+ newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
+ }
+ for (Iterator iterator = fieldAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands newAttributeBandSet = (NewAttributeBands) iterator.next();
+ newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
+ }
+ for (Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
+ NewAttributeBands newAttributeBandSet = (NewAttributeBands) iterator.next();
+ newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
+ }
+ }
+
+ private void renumberBci(List list, IntList bciRenumbering, Map labelsToOffsets) {
+ for (int i = list.size() - 1; i >= 0; i--) {
+ Object label = list.get(i);
+ if (label instanceof Integer) {
+ break;
+ } else if (label instanceof Label) {
+ list.remove(i);
+ Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
+ list.add(i, new Integer(bciRenumbering.get(bytecodeIndex.intValue())));
+ }
+ }
+ }
+
+ private void renumberOffsetBci(List relative, List list,
+ IntList bciRenumbering, Map labelsToOffsets) {
+ for (int i = list.size() - 1; i >= 0; i--) {
+ Object label = list.get(i);
+ if (label instanceof Integer) {
+ break;
+ } else if (label instanceof Label) {
+ list.remove(i);
+ Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
+ Integer renumberedOffset = new Integer(bciRenumbering
+ .get(bytecodeIndex.intValue())
+ - ((Integer) relative.get(i)).intValue());
+ list.add(i, renumberedOffset);
+ }
+ }
+ }
+
+ private void renumberDoubleOffsetBci(List relative, List firstOffset, List list,
+ IntList bciRenumbering, Map labelsToOffsets) {
+ // TODO: There's probably a nicer way of doing this...
+ for (int i = list.size() - 1; i >= 0; i--) {
+ Object label = list.get(i);
+ if (label instanceof Integer) {
+ break;
+ } else if (label instanceof Label) {
+ list.remove(i);
+ Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
+ Integer renumberedOffset = new Integer(bciRenumbering
+ .get(bytecodeIndex.intValue())
+ - ((Integer) relative.get(i)).intValue() - ((Integer) firstOffset.get(i)).intValue());
+ list.add(i, renumberedOffset);
+ }
+ }
+ }
+
+ public boolean isAnySyntheticClasses() {
+ return anySyntheticClasses;
+ }
+
+ public boolean isAnySyntheticFields() {
+ return anySyntheticFields;
+ }
+
+ public boolean isAnySyntheticMethods() {
+ return anySyntheticMethods;
+ }
+
+ public void addParameterAnnotation(int parameter, String desc,
+ boolean visible, List nameRU, List t, List values, List caseArrayN, List nestTypeRS, List nestNameRU, List nestPairN) {
+ if(visible) {
+ if(tempMethodRVPA == null) {
+ tempMethodRVPA = new TempParamAnnotation(numMethodArgs);
+ tempMethodRVPA.addParameterAnnotation(parameter, desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+ }
+ Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
+ tempMethodFlags.add(new Long(flag.longValue() | (1<<23)));
+ } else {
+ if(tempMethodRIPA == null) {
+ tempMethodRIPA = new TempParamAnnotation(numMethodArgs);
+ tempMethodRIPA.addParameterAnnotation(parameter, desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+ }
+ Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
+ tempMethodFlags.add(new Long(flag.longValue() | (1<<24)));
+ }
+ }
+
+ private static class TempParamAnnotation {
+
+ int numParams;
+ int[] annoN;
+ IntList pairN = new IntList();
+ List typeRS = new ArrayList();
+ List nameRU = new ArrayList();
+ List t = new ArrayList();
+ List values = new ArrayList();
+ List caseArrayN = new ArrayList();
+ List nestTypeRS = new ArrayList();
+ List nestNameRU = new ArrayList();
+ List nestPairN = new ArrayList();
+
+ public TempParamAnnotation (int numParams) {
+ this.numParams = numParams;
+ annoN = new int[numParams];
+ }
+
+ public void addParameterAnnotation(int parameter, String desc,
+ List nameRU, List t, List values, List caseArrayN,
+ List nestTypeRS, List nestNameRU, List nestPairN) {
+ annoN[parameter]++;
+ typeRS.add(desc);
+ pairN.add(nameRU.size());
+ this.nameRU.addAll(nameRU);
+ this.t.addAll(t);
+ this.values.addAll(values);
+ this.caseArrayN.addAll(caseArrayN);
+ this.nestTypeRS.addAll(nestTypeRS);
+ this.nestNameRU.addAll(nestNameRU);
+ this.nestPairN.addAll(nestPairN);
+ }
+ }
+
+ public void addAnnotation(int context, String desc, boolean visible, List nameRU, List t, List values, List caseArrayN, List nestTypeRS, List nestNameRU, List nestPairN) {
+ switch (context) {
+ case MetadataBandGroup.CONTEXT_CLASS:
+ if(visible) {
+ class_RVA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+ if((class_flags[index] & (1<<21)) != 0) {
+ class_RVA_bands.incrementAnnoN();
+ } else {
+ class_RVA_bands.newEntryInAnnoN();
+ class_flags[index] = class_flags[index] | (1<<21);
+ }
+ } else {
+ class_RIA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+ if((class_flags[index] & (1<<22)) != 0) {
+ class_RIA_bands.incrementAnnoN();
+ } else {
+ class_RIA_bands.newEntryInAnnoN();
+ class_flags[index] = class_flags[index] | (1<<22);
+ }
+ }
+ break;
+ case MetadataBandGroup.CONTEXT_FIELD:
+ if(visible) {
+ field_RVA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+ Long flag = (Long) tempFieldFlags.remove(tempFieldFlags.size() - 1);
+ if((flag.intValue() & (1<<21)) != 0) {
+ field_RVA_bands.incrementAnnoN();
+ } else {
+ field_RVA_bands.newEntryInAnnoN();
+ }
+ tempFieldFlags.add(new Long(flag.intValue() | (1<<21)));
+ } else {
+ field_RIA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+ Long flag = (Long) tempFieldFlags.remove(tempFieldFlags.size() - 1);
+ if((flag.intValue() & (1<<22)) != 0) {
+ field_RIA_bands.incrementAnnoN();
+ } else {
+ field_RIA_bands.newEntryInAnnoN();
+ }
+ tempFieldFlags.add(new Long(flag.intValue() | (1<<22)));
+ }
+ break;
+ case MetadataBandGroup.CONTEXT_METHOD:
+ if(visible) {
+ method_RVA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+ Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
+ if((flag.intValue() & (1<<21)) != 0) {
+ method_RVA_bands.incrementAnnoN();
+ } else {
+ method_RVA_bands.newEntryInAnnoN();
+ }
+ tempMethodFlags.add(new Long(flag.intValue() | (1<<21)));
+ } else {
+ method_RIA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+ Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
+ if((flag.intValue() & (1<<22)) != 0) {
+ method_RIA_bands.incrementAnnoN();
+ } else {
+ method_RIA_bands.newEntryInAnnoN();
+ }
+ tempMethodFlags.add(new Long(flag.intValue() | (1<<22)));
+ }
+ break;
+ }
+ }
+
+ public void addAnnotationDefault(List nameRU, List t, List values, List caseArrayN, List nestTypeRS, List nestNameRU, List nestPairN) {
+ method_AD_bands.addAnnotation(null, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+ Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
+ tempMethodFlags.add(new Long(flag.longValue() | (1<<25)));
+ }
+
+ /**
+ * Remove all entries for the current class
+ */
+ public void removeCurrentClass() {
+ // Note - this doesn't remove any entries added to the constant pool but
+ // that shouldn't be a problem
+ if ((class_flags[index] & (1 << 17)) != 0) {
+ classSourceFile.remove(classSourceFile.size() - 1);
+ }
+ if ((class_flags[index] & (1 << 18)) != 0) {
+ classEnclosingMethodClass
+ .remove(classEnclosingMethodClass.size() - 1);
+ classEnclosingMethodDesc
+ .remove(classEnclosingMethodDesc.size() - 1);
+ }
+ if ((class_flags[index] & (1 << 19)) != 0) {
+ classSignature.remove(classSignature.size() - 1);
+ }
+ if ((class_flags[index] & (1 << 21)) != 0) {
+ class_RVA_bands.removeLatest();
+ }
+ if ((class_flags[index] & (1 << 22)) != 0) {
+ class_RIA_bands.removeLatest();
+ }
+ for (Iterator iterator = tempFieldFlags.iterator(); iterator.hasNext();) {
+ Long flagsL = (Long) iterator.next();
+ long flags = flagsL.longValue();
+ if ((flags & (1 << 19)) != 0) {
+ fieldSignature.remove(fieldSignature.size() - 1);
+ }
+ if ((flags & (1 << 17)) != 0) {
+ fieldConstantValueKQ.remove(fieldConstantValueKQ.size() - 1);
+ }
+ if ((flags & (1 << 21)) != 0) {
+ field_RVA_bands.removeLatest();
+ }
+ if ((flags & (1 << 22)) != 0) {
+ field_RIA_bands.removeLatest();
+ }
+ }
+ for (Iterator iterator = tempMethodFlags.iterator(); iterator.hasNext();) {
+ Long flagsL = (Long) iterator.next();
+ long flags = flagsL.longValue();
+ if ((flags & (1 << 19)) != 0) {
+ methodSignature.remove(methodSignature.size() - 1);
+ }
+ if ((flags & (1 << 18)) != 0) {
+ int exceptions = methodExceptionNumber
+ .remove(methodExceptionNumber.size() - 1);
+ for (int i = 0; i < exceptions; i++) {
+ methodExceptionClasses
+ .remove(methodExceptionClasses.size() - 1);
+ }
+ }
+ if ((flags & (1 << 17)) != 0) { // has code attribute
+ codeMaxLocals.remove(codeMaxLocals.size() - 1);
+ codeMaxStack.remove(codeMaxStack.size() - 1);
+ int handlers = codeHandlerCount
+ .remove(codeHandlerCount.size() - 1);
+ for (int i = 0; i < handlers; i++) {
+ int index = codeHandlerStartP.size() - 1;
+ codeHandlerStartP.remove(index);
+ codeHandlerEndPO.remove(index);
+ codeHandlerCatchPO.remove(index);
+ codeHandlerClass.remove(index);
+ }
+ if (!stripDebug) {
+ long cdeFlags = ((Long) codeFlags
+ .remove(codeFlags.size() - 1)).longValue();
+ int numLocalVariables = codeLocalVariableTableN
+ .remove(codeLocalVariableTableN.size() - 1);
+ for (int i = 0; i < numLocalVariables; i++) {
+ int location = codeLocalVariableTableBciP.size() - 1;
+ codeLocalVariableTableBciP.remove(location);
+ codeLocalVariableTableSpanO.remove(location);
+ codeLocalVariableTableNameRU.remove(location);
+ codeLocalVariableTableTypeRS.remove(location);
+ codeLocalVariableTableSlot.remove(location);
+ }
+ if ((cdeFlags & (1 << 3)) != 0) {
+ int numLocalVariablesInTypeTable = codeLocalVariableTypeTableN
+ .remove(codeLocalVariableTypeTableN.size() - 1);
+ for (int i = 0; i < numLocalVariablesInTypeTable; i++) {
+ int location = codeLocalVariableTypeTableBciP
+ .size() - 1;
+ codeLocalVariableTypeTableBciP.remove(location);
+ codeLocalVariableTypeTableSpanO.remove(location);
+ codeLocalVariableTypeTableNameRU.remove(location);
+ codeLocalVariableTypeTableTypeRS.remove(location);
+ codeLocalVariableTypeTableSlot.remove(location);
+ }
+ }
+ if ((cdeFlags & (1 << 1)) != 0) {
+ int numLineNumbers = codeLineNumberTableN
+ .remove(codeLineNumberTableN.size() - 1);
+ for (int i = 0; i < numLineNumbers; i++) {
+ int location = codeLineNumberTableBciP.size() - 1;
+ codeLineNumberTableBciP.remove(location);
+ codeLineNumberTableLine.remove(location);
+ }
+ }
+ }
+ }
+ if ((flags & (1 << 21)) != 0) {
+ method_RVA_bands.removeLatest();
+ }
+ if ((flags & (1 << 22)) != 0) {
+ method_RIA_bands.removeLatest();
+ }
+ if ((flags & (1 << 23)) != 0) {
+ method_RVPA_bands.removeLatest();
+ }
+ if ((flags & (1 << 24)) != 0) {
+ method_RIPA_bands.removeLatest();
+ }
+ if ((flags & (1 << 25)) != 0) {
+ method_AD_bands.removeLatest();
+ }
+ }
+ class_this[index] = null;
+ class_super[index] = null;
+ class_interface_count[index] = 0;
+ class_interface[index] = null;
+ major_versions[index] = 0;
+ class_flags[index] = 0;
+ tempFieldDesc.clear();
+ tempFieldFlags.clear();
+ tempMethodDesc.clear();
+ tempMethodFlags.clear();
+ if(index > 0) {
+ index--;
+ }
+ }
+
+ public int numClassesProcessed() {
+ return index;
+ }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/Codec.java b/src/main/java/org/apache/harmony/pack200/Codec.java
new file mode 100644
index 0000000..83b80e0
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/Codec.java
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * A Codec allows a sequence of bytes to be decoded into integer values (or vice
+ * versa).
+ *
+ * There are a number of standard Codecs ({@link #UDELTA5}, {@link #UNSIGNED5},
+ * {@link #BYTE1}, {@link #CHAR3}) that are used in the implementation of many
+ * bands; but there are a variety of other ones, and indeed the specification
+ * assumes that other combinations of values can result in more specific and
+ * efficient formats. There are also a sequence of canonical encodings defined
+ * by the Pack200 specification, which allow a Codec to be referred to by
+ * canonical number. {@link CodecEncoding#getCodec(int, InputStream, Codec)})
+ */
+public abstract class Codec {
+
+ /**
+ * BCI5 = (5,4): Used for storing branching information in bytecode.
+ */
+ public static final BHSDCodec BCI5 = new BHSDCodec(5, 4);
+
+ /**
+ * BRANCH5 = (5,4,2): Used for storing branching information in bytecode.
+ */
+ public static final BHSDCodec BRANCH5 = new BHSDCodec(5, 4, 2);
+
+ /**
+ * BYTE1 = (1,256): Used for storing plain bytes.
+ */
+ public static final BHSDCodec BYTE1 = new BHSDCodec(1, 256);
+
+ /**
+ * CHAR3 = (3,128): Used for storing text (UTF-8) strings. NB This isn't
+ * quite the same as UTF-8, but has similar properties; ASCII characters
+ * < 127 are stored in a single byte.
+ */
+ public static final BHSDCodec CHAR3 = new BHSDCodec(3, 128);
+
+ /**
+ * DELTA5 = (5,64,1,1): Used for the majority of numerical codings where
+ * there is a correlated sequence of signed values.
+ */
+ public static final BHSDCodec DELTA5 = new BHSDCodec(5, 64, 1, 1);
+
+ /**
+ * MDELTA5 = (5,64,2,1): Used for the majority of numerical codings where
+ * there is a correlated sequence of signed values, but where most of them
+ * are expected to be non-negative.
+ */
+ public static final BHSDCodec MDELTA5 = new BHSDCodec(5, 64, 2, 1);
+
+ /**
+ * SIGNED5 = (5,64,1): Used for small signed values.
+ */
+ public static final BHSDCodec SIGNED5 = new BHSDCodec(5, 64, 1);
+
+ /**
+ * UDELTA5 = (5,64,0,1): Used for the majority of numerical codings where
+ * there is a correlated sequence of unsigned values.
+ */
+ public static final BHSDCodec UDELTA5 = new BHSDCodec(5, 64, 0, 1);
+
+ /**
+ * UNSIGNED5 = (5,64): Used for small unsigned values.
+ */
+ public static final BHSDCodec UNSIGNED5 = new BHSDCodec(5, 64);
+
+ public int lastBandLength;
+
+ /**
+ * Decode a sequence of bytes from the given input stream, returning the
+ * value as a long. Note that this method can only be applied for non-delta
+ * encodings.
+ *
+ * @param in
+ * the input stream to read from
+ * @return the value as a long
+ * @throws IOException
+ * if there is a problem reading from the underlying input
+ * stream
+ * @throws Pack200Exception
+ * if the encoding is a delta encoding
+ */
+ public abstract int decode(InputStream in) throws IOException,
+ Pack200Exception;
+
+ /**
+ * Encode a single value into a sequence of bytes.
+ *
+ * @param value
+ * the value to encode
+ * @param last
+ * the previous value encoded (for delta encodings)
+ * @return the encoded bytes
+ * @throws Pack200Exception
+ */
+ public abstract byte[] encode(int value, int last)
+ throws Pack200Exception;
+
+ /**
+ * Encode a single value into a sequence of bytes. Note that this method can
+ * only be used for non-delta encodings.
+ *
+ * @param value
+ * the value to encode
+ * @return the encoded bytes
+ * @throws Pack200Exception
+ */
+ public abstract byte[] encode(int value) throws Pack200Exception;
+
+ /**
+ * Decode a sequence of bytes from the given input stream, returning the
+ * value as a long. If this encoding is a delta encoding (d=1) then the
+ * previous value must be passed in as a parameter. If it is a non-delta
+ * encoding, then it does not matter what value is passed in, so it makes
+ * sense for the value to be passed in by default using code similar to:
+ *
+ * <pre>
+ * long last = 0;
+ * while (condition) {
+ * last = codec.decode(in, last);
+ * // do something with last
+ * }
+ * </pre>
+ *
+ * @param in
+ * the input stream to read from
+ * @param last
+ * the previous value read, which must be supplied if the codec
+ * is a delta encoding
+ * @return the value as a long
+ * @throws IOException
+ * if there is a problem reading from the underlying input
+ * stream
+ * @throws Pack200Exception
+ * if there is a problem decoding the value or that the value is
+ * invalid
+ */
+ public abstract int decode(InputStream in, long last) throws IOException,
+ Pack200Exception;
+
+ /**
+ * Decodes a sequence of <code>n</code> values from <code>in</code>.
+ * This should probably be used in most cases, since some codecs (such as
+ *
+ * @{link PopCodec}) only work when the number of values to be read is
+ * known.
+ *
+ * @param n
+ * the number of values to decode
+ * @param in
+ * the input stream to read from
+ * @return an array of <code>int</code> values corresponding to values
+ * decoded
+ * @throws IOException
+ * if there is a problem reading from the underlying input
+ * stream
+ * @throws Pack200Exception
+ * if there is a problem decoding the value or that the value is
+ * invalid
+ */
+ public int[] decodeInts(int n, InputStream in) throws IOException,
+ Pack200Exception {
+ lastBandLength = 0;
+ int result[] = new int[n];
+ int last = 0;
+ for (int i = 0; i < n; i++) {
+ result[i] = last = decode(in, last);
+ }
+ return result;
+ }
+
+ /**
+ * Decodes a sequence of <code>n</code> values from <code>in</code>.
+ *
+ * @param n
+ * the number of values to decode
+ * @param in
+ * the input stream to read from
+ * @param firstValue
+ * the first value in the band if it has already been read
+ * @return an array of <code>int</code> values corresponding to values
+ * decoded, with firstValue as the first value in the array.
+ * @throws IOException
+ * if there is a problem reading from the underlying input
+ * stream
+ * @throws Pack200Exception
+ * if there is a problem decoding the value or that the value is
+ * invalid
+ */
+ public int[] decodeInts(int n, InputStream in, int firstValue)
+ throws IOException, Pack200Exception {
+ int result[] = new int[n + 1];
+ result[0] = firstValue;
+ int last = firstValue;
+ for (int i = 1; i < n + 1; i++) {
+ result[i] = last = decode(in, last);
+ }
+ return result;
+ }
+
+ /**
+ * Encode a sequence of integers into a byte array
+ *
+ * @param ints
+ * the values to encode
+ * @return byte[] encoded bytes
+ * @throws Pack200Exception
+ * if there is a problem encoding any of the values
+ */
+ public byte[] encode(int[] ints) throws Pack200Exception {
+ int total = 0;
+ byte[][] bytes = new byte[ints.length][];
+ for (int i = 0; i < ints.length; i++) {
+ bytes[i] = encode(ints[i], i > 0 ? ints[i-1] : 0);
+ total += bytes[i].length;
+ }
+ byte[] encoded = new byte[total];
+ int index = 0;
+ for (int i = 0; i < bytes.length; i++) {
+ System.arraycopy(bytes[i], 0, encoded, index, bytes[i].length);
+ index += bytes[i].length;
+ }
+ return encoded;
+ }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CodecEncoding.java b/src/main/java/org/apache/harmony/pack200/CodecEncoding.java
new file mode 100644
index 0000000..c7df8a4
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CodecEncoding.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * CodecEncoding is used to get the right Codec for a given meta-encoding
+ */
+public class CodecEncoding {
+
+ /**
+ * The canonical encodings are defined to allow a single byte to represent
+ * one of the standard encodings. The following values are defined in the
+ * Pack200 specification, and this array cannot be changed.
+ */
+ private static final BHSDCodec[] canonicalCodec = { null,
+ new BHSDCodec(1, 256), new BHSDCodec(1, 256, 1),
+ new BHSDCodec(1, 256, 0, 1), new BHSDCodec(1, 256, 1, 1),
+ new BHSDCodec(2, 256), new BHSDCodec(2, 256, 1),
+ new BHSDCodec(2, 256, 0, 1), new BHSDCodec(2, 256, 1, 1),
+ new BHSDCodec(3, 256), new BHSDCodec(3, 256, 1),
+ new BHSDCodec(3, 256, 0, 1), new BHSDCodec(3, 256, 1, 1),
+ new BHSDCodec(4, 256), new BHSDCodec(4, 256, 1),
+ new BHSDCodec(4, 256, 0, 1), new BHSDCodec(4, 256, 1, 1),
+ new BHSDCodec(5, 4), new BHSDCodec(5, 4, 1),
+ new BHSDCodec(5, 4, 2), new BHSDCodec(5, 16),
+ new BHSDCodec(5, 16, 1), new BHSDCodec(5, 16, 2),
+ new BHSDCodec(5, 32), new BHSDCodec(5, 32, 1),
+ new BHSDCodec(5, 32, 2), new BHSDCodec(5, 64),
+ new BHSDCodec(5, 64, 1), new BHSDCodec(5, 64, 2),
+ new BHSDCodec(5, 128), new BHSDCodec(5, 128, 1),
+ new BHSDCodec(5, 128, 2), new BHSDCodec(5, 4, 0, 1),
+ new BHSDCodec(5, 4, 1, 1), new BHSDCodec(5, 4, 2, 1),
+ new BHSDCodec(5, 16, 0, 1), new BHSDCodec(5, 16, 1, 1),
+ new BHSDCodec(5, 16, 2, 1), new BHSDCodec(5, 32, 0, 1),
+ new BHSDCodec(5, 32, 1, 1), new BHSDCodec(5, 32, 2, 1),
+ new BHSDCodec(5, 64, 0, 1), new BHSDCodec(5, 64, 1, 1),
+ new BHSDCodec(5, 64, 2, 1), new BHSDCodec(5, 128, 0, 1),
+ new BHSDCodec(5, 128, 1, 1), new BHSDCodec(5, 128, 2, 1),
+ new BHSDCodec(2, 192), new BHSDCodec(2, 224),
+ new BHSDCodec(2, 240), new BHSDCodec(2, 248),
+ new BHSDCodec(2, 252), new BHSDCodec(2, 8, 0, 1),
+ new BHSDCodec(2, 8, 1, 1), new BHSDCodec(2, 16, 0, 1),
+ new BHSDCodec(2, 16, 1, 1), new BHSDCodec(2, 32, 0, 1),
+ new BHSDCodec(2, 32, 1, 1), new BHSDCodec(2, 64, 0, 1),
+ new BHSDCodec(2, 64, 1, 1), new BHSDCodec(2, 128, 0, 1),
+ new BHSDCodec(2, 128, 1, 1), new BHSDCodec(2, 192, 0, 1),
+ new BHSDCodec(2, 192, 1, 1), new BHSDCodec(2, 224, 0, 1),
+ new BHSDCodec(2, 224, 1, 1), new BHSDCodec(2, 240, 0, 1),
+ new BHSDCodec(2, 240, 1, 1), new BHSDCodec(2, 248, 0, 1),
+ new BHSDCodec(2, 248, 1, 1), new BHSDCodec(3, 192),
+ new BHSDCodec(3, 224), new BHSDCodec(3, 240),
+ new BHSDCodec(3, 248), new BHSDCodec(3, 252),
+ new BHSDCodec(3, 8, 0, 1), new BHSDCodec(3, 8, 1, 1),
+ new BHSDCodec(3, 16, 0, 1), new BHSDCodec(3, 16, 1, 1),
+ new BHSDCodec(3, 32, 0, 1), new BHSDCodec(3, 32, 1, 1),
+ new BHSDCodec(3, 64, 0, 1), new BHSDCodec(3, 64, 1, 1),
+ new BHSDCodec(3, 128, 0, 1), new BHSDCodec(3, 128, 1, 1),
+ new BHSDCodec(3, 192, 0, 1), new BHSDCodec(3, 192, 1, 1),
+ new BHSDCodec(3, 224, 0, 1), new BHSDCodec(3, 224, 1, 1),
+ new BHSDCodec(3, 240, 0, 1), new BHSDCodec(3, 240, 1, 1),
+ new BHSDCodec(3, 248, 0, 1), new BHSDCodec(3, 248, 1, 1),
+ new BHSDCodec(4, 192), new BHSDCodec(4, 224),
+ new BHSDCodec(4, 240), new BHSDCodec(4, 248),
+ new BHSDCodec(4, 252), new BHSDCodec(4, 8, 0, 1),
+ new BHSDCodec(4, 8, 1, 1), new BHSDCodec(4, 16, 0, 1),
+ new BHSDCodec(4, 16, 1, 1), new BHSDCodec(4, 32, 0, 1),
+ new BHSDCodec(4, 32, 1, 1), new BHSDCodec(4, 64, 0, 1),
+ new BHSDCodec(4, 64, 1, 1), new BHSDCodec(4, 128, 0, 1),
+ new BHSDCodec(4, 128, 1, 1), new BHSDCodec(4, 192, 0, 1),
+ new BHSDCodec(4, 192, 1, 1), new BHSDCodec(4, 224, 0, 1),
+ new BHSDCodec(4, 224, 1, 1), new BHSDCodec(4, 240, 0, 1),
+ new BHSDCodec(4, 240, 1, 1), new BHSDCodec(4, 248, 0, 1),
+ new BHSDCodec(4, 248, 1, 1) };
+
+ private static Map canonicalCodecsToSpecifiers;
+
+
+ /**
+ * Returns the codec specified by the given value byte and optional byte
+ * header. If the value is >=116, then bytes may be consumed from the
+ * secondary input stream, which is taken to be the contents of the
+ * band_headers byte array. Since the values from this are consumed and not
+ * repeated, the input stream should be reused for subsequent encodings.
+ * This does not therefore close the input stream.
+ *
+ * @param value
+ * the canonical encoding value
+ * @param in
+ * the input stream to read additional byte headers from
+ * @param defaultCodec
+ * TODO
+ * @return the corresponding codec, or <code>null</code> if the default
+ * should be used
+ *
+ * @throws IOException
+ * if there is a problem reading from the input stream (which in
+ * reality, is never, since the band_headers are likely stored
+ * in a byte array and accessed via a ByteArrayInputStream.
+ * However, an EOFException could occur if things go wrong)
+ * @throws Pack200Exception
+ */
+ public static Codec getCodec(int value, InputStream in, Codec defaultCodec)
+ throws IOException, Pack200Exception {
+ // Sanity check to make sure that no-one has changed
+ // the canonical codecs, which would really cause havoc
+ if (canonicalCodec.length != 116) {
+ throw new Error(
+ "Canonical encodings have been incorrectly modified");
+ }
+ if (value < 0) {
+ throw new IllegalArgumentException(
+ "Encoding cannot be less than zero");
+ } else if (value == 0) {
+ return defaultCodec;
+ } else if (value <= 115) {
+ return canonicalCodec[value];
+ } else if (value == 116) {
+ int code = in.read();
+ if (code == -1) {
+ throw new EOFException(
+ "End of buffer read whilst trying to decode codec");
+ }
+ int d = (code & 0x01);
+ int s = (code >> 1 & 0x03);
+ int b = (code >> 3 & 0x07) + 1; // this might result in an invalid
+ // number, but it's checked in the
+ // Codec constructor
+ code = in.read();
+ if (code == -1) {
+ throw new EOFException(
+ "End of buffer read whilst trying to decode codec");
+ }
+ int h = code + 1;
+ // This handles the special cases for invalid combinations of data.
+ return new BHSDCodec(b, h, s, d);
+ } else if (value >= 117 && value <= 140) { // Run codec
+ int offset = value - 117;
+ int kx = offset & 3;
+ boolean kbflag = (offset >> 2 & 1) == 1;
+ boolean adef = (offset >> 3 & 1) == 1;
+ boolean bdef = (offset >> 4 & 1) == 1;
+ // If both A and B use the default encoding, what's the point of
+ // having a run of default values followed by default values
+ if (adef && bdef) {
+ throw new Pack200Exception(
+ "ADef and BDef should never both be true");
+ }
+ int kb = (kbflag ? in.read() : 3);
+ int k = (kb + 1) * (int) Math.pow(16, kx);
+ Codec aCodec, bCodec;
+ if (adef) {
+ aCodec = defaultCodec;
+ } else {
+ aCodec = getCodec(in.read(), in, defaultCodec);
+ }
+ if (bdef) {
+ bCodec = defaultCodec;
+ } else {
+ bCodec = getCodec(in.read(), in, defaultCodec);
+ }
+ return new RunCodec(k, aCodec, bCodec);
+ } else if (value >= 141 && value <= 188) { // Population Codec
+ int offset = value - 141;
+ boolean fdef = (offset & 1) == 1;
+ boolean udef = (offset >> 1 & 1) == 1;
+ int tdefl = offset >> 2;
+ boolean tdef = tdefl != 0;
+ // From section 6.7.3 of spec
+ final int[] tdefToL = { 0, 4, 8, 16, 32, 64, 128, 192, 224, 240,
+ 248, 252 };
+ int l = tdefToL[tdefl];
+ // NOTE: Do not re-factor this to bring out uCodec; the order in
+ // which
+ // they are read from the stream is important
+ if (tdef) {
+ Codec fCodec = (fdef ? defaultCodec : getCodec(in.read(), in,
+ defaultCodec));
+ Codec uCodec = (udef ? defaultCodec : getCodec(in.read(), in,
+ defaultCodec));
+ // Unfortunately, if tdef, then tCodec depends both on l and
+ // also on k, the
+ // number of items read from the fCodec. So we don't know in
+ // advance what
+ // the codec will be.
+ return new PopulationCodec(fCodec, l, uCodec);
+ } else {
+ Codec fCodec = (fdef ? defaultCodec : getCodec(in.read(), in,
+ defaultCodec));
+ Codec tCodec = getCodec(in.read(), in, defaultCodec);
+ Codec uCodec = (udef ? defaultCodec : getCodec(in.read(), in,
+ defaultCodec));
+ return new PopulationCodec(fCodec, tCodec, uCodec);
+ }
+ } else {
+ throw new Pack200Exception("Invalid codec encoding byte (" + value
+ + ") found");
+ }
+ }
+
+ public static int getSpecifierForDefaultCodec(BHSDCodec defaultCodec) {
+ return getSpecifier(defaultCodec, null)[0];
+ }
+
+ public static int[] getSpecifier(Codec codec, Codec defaultForBand) {
+ // lazy initialization
+ if(canonicalCodecsToSpecifiers == null) {
+ HashMap reverseMap = new HashMap(canonicalCodec.length);
+ for (int i = 0; i < canonicalCodec.length; i++) {
+ reverseMap.put(canonicalCodec[i], new Integer(i));
+ }
+ canonicalCodecsToSpecifiers = reverseMap;
+ }
+
+ if(canonicalCodecsToSpecifiers.containsKey(codec)) {
+ return new int[] {((Integer)canonicalCodecsToSpecifiers.get(codec)).intValue()};
+ } else if (codec instanceof BHSDCodec) {
+ // Cache these?
+ BHSDCodec bhsdCodec = (BHSDCodec)codec;
+ int[] specifiers = new int[3];
+ specifiers[0] = 116;
+ specifiers[1] = (bhsdCodec.isDelta() ? 1 : 0) + 2
+ * bhsdCodec.getS() + 8 * (bhsdCodec.getB()-1);
+ specifiers[2] = bhsdCodec.getH() - 1;
+ return specifiers;
+ } else if (codec instanceof RunCodec) {
+ RunCodec runCodec = (RunCodec) codec;
+ int k = runCodec.getK();
+ int kb;
+ int kx;
+ if(k <= 256) {
+ kb = 0;
+ kx = k - 1;
+ } else if (k <= 4096) {
+ kb = 1;
+ kx = k/16 - 1;
+ } else if (k <= 65536) {
+ kb = 2;
+ kx = k/256 - 1;
+ } else {
+ kb = 3;
+ kx = k/4096 - 1;
+ }
+ Codec aCodec = runCodec.getACodec();
+ Codec bCodec = runCodec.getBCodec();
+ int abDef = 0;
+ if(aCodec.equals(defaultForBand)) {
+ abDef = 1;
+ } else if (bCodec.equals(defaultForBand)) {
+ abDef = 2;
+ }
+ int first = 117 + kb + (kx==3 ? 0 : 4) + (8 * abDef);
+ int[] aSpecifier = abDef == 1 ? new int[0] : getSpecifier(aCodec, defaultForBand);
+ int[] bSpecifier = abDef == 2 ? new int[0] : getSpecifier(bCodec, defaultForBand);
+ int[] specifier = new int[1 + (kx==3 ? 0 : 1) + aSpecifier.length + bSpecifier.length];
+ specifier[0] = first;
+ int index = 1;
+ if(kx != 3) {
+ specifier[1] = kx;
+ index++;
+ }
+ for (int i = 0; i < aSpecifier.length; i++) {
+ specifier[index] = aSpecifier[i];
+ index++;
+ }
+ for (int i = 0; i < bSpecifier.length; i++) {
+ specifier[index] = bSpecifier[i];
+ index++;
+ }
+ return specifier;
+ } else if (codec instanceof PopulationCodec) {
+ PopulationCodec populationCodec = (PopulationCodec) codec;
+ Codec tokenCodec = populationCodec.getTokenCodec();
+ Codec favouredCodec = populationCodec.getFavouredCodec();
+ Codec unfavouredCodec = populationCodec.getUnfavouredCodec();
+ int fDef = favouredCodec.equals(defaultForBand) ? 1 : 0;
+ int uDef = unfavouredCodec.equals(defaultForBand) ? 1 : 0;
+ int tDefL = 0;
+ int[] favoured = populationCodec.getFavoured();
+ if(favoured != null) {
+ int k = favoured.length;
+ if(tokenCodec == Codec.BYTE1) {
+ tDefL = 1;
+ } else if (tokenCodec instanceof BHSDCodec) {
+ BHSDCodec tokenBHSD = (BHSDCodec) tokenCodec;
+ if(tokenBHSD.getS() == 0) {
+ int[] possibleLValues = new int[] {4, 8, 16, 32, 64, 128, 192, 224, 240, 248, 252};
+ int l = 256-tokenBHSD.getH();
+ int index = Arrays.binarySearch(possibleLValues, l);
+ if(index != -1) {
+ // TODO: check range is ok for ks
+ tDefL = index++;
+ }
+ }
+ }
+ }
+ int first = 141 + fDef + (2 * uDef) + (4 * tDefL);
+ int[] favouredSpecifier = fDef == 1 ? new int[0] : getSpecifier(favouredCodec, defaultForBand);
+ int[] tokenSpecifier = tDefL != 0 ? new int[0] : getSpecifier(tokenCodec, defaultForBand);
+ int[] unfavouredSpecifier = uDef == 1 ? new int[0] : getSpecifier(unfavouredCodec, defaultForBand);
+ int[] specifier = new int[1 + favouredSpecifier.length + unfavouredSpecifier.length + tokenSpecifier.length];
+ specifier[0] = first;
+ int index = 1;
+ for (int i = 0; i < favouredSpecifier.length; i++) {
+ specifier[index] = favouredSpecifier[i];
+ index++;
+ }
+ for (int i = 0; i < tokenSpecifier.length; i++) {
+ specifier[index] = tokenSpecifier[i];
+ index++;
+ }
+ for (int i = 0; i < unfavouredSpecifier.length; i++) {
+ specifier[index] = unfavouredSpecifier[i];
+ index++;
+ }
+ return specifier;
+ }
+
+ return null;
+ }
+
+ public static BHSDCodec getCanonicalCodec(int i) {
+ return canonicalCodec[i];
+ }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/ConstantPoolEntry.java b/src/main/java/org/apache/harmony/pack200/ConstantPoolEntry.java
new file mode 100644
index 0000000..d2fd1b3
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/ConstantPoolEntry.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Abstract superclass for constant pool entries
+ */
+public abstract class ConstantPoolEntry {
+
+ private int index = -1;
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CpBands.java b/src/main/java/org/apache/harmony/pack200/CpBands.java
new file mode 100644
index 0000000..aad5037
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CpBands.java
@@ -0,0 +1,686 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You 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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.objectweb.asm.Type;
+
+/**
+ * Pack200 Constant Pool Bands
+ */
+public class CpBands extends BandSet {
+
+ // Don't need to include default attribute names in the constant pool bands
+ private final Set defaultAttributeNames = new HashSet();
+
+ private final Set cp_Utf8 = new TreeSet();
+ private final Set cp_Int = new TreeSet();
+ private final Set cp_Float = new TreeSet();
+ private final Set cp_Long = new TreeSet();
+ private final Set cp_Double = new TreeSet();
+ private final Set cp_String = new TreeSet();
+ private final Set cp_Class = new TreeSet();
+ private final Set cp_Signature = new TreeSet();
+ private final Set cp_Descr = new TreeSet();
+ private final Set cp_Field = new TreeSet();
+ private final Set cp_Method = new TreeSet();
+ private final Set cp_Imethod = new TreeSet();
+
+ private final Map stringsToCpUtf8 = new HashMap();
+ private final Map stringsToCpNameAndType = new HashMap();
+ private final Map stringsToCpClass = new HashMap();
+ private final Map stringsToCpSignature = new HashMap();
+ private final Map stringsToCpMethod = new HashMap();
+ private final Map stringsToCpField = new HashMap();
+ private final Map stringsToCpIMethod = new HashMap();
+
+ private final Map objectsToCPConstant = new HashMap();
+
+ private final Segment segment;
+
+ public CpBands(Segment segment, int effort) {
+ super(effort, segment.getSegmentHeader());
+ this.segment = segment;
+ defaultAttributeNames.add("AnnotationDefault");
+ defaultAttributeNames.add("RuntimeVisibleAnnotations");
+ defaultAttributeNames.add("RuntimeInvisibleAnnotations");
+ defaultAttributeNames.add("RuntimeVisibleParameterAnnotations");
+ defaultAttributeNames.add("RuntimeInvisibleParameterAnnotations");
+ defaultAttributeNames.add("Code");
+ defaultAttributeNames.add("LineNumberTable");
+ defaultAttributeNames.add("LocalVariableTable");
+ defaultAttributeNames.add("LocalVariableTypeTable");
+ defaultAttributeNames.add("ConstantValue");
+ defaultAttributeNames.add("Deprecated");
+ defaultAttributeNames.add("EnclosingMethod");
+ defaultAttributeNames.add("Exceptions");
+ defaultAttributeNames.add("InnerClasses");
+ defaultAttributeNames.add("Signature");
+ defaultAttributeNames.add("SourceFile");
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ PackingUtils.log("Writing constant pool bands...");
+ writeCpUtf8(out);
+ writeCpInt(out);
+ writeCpFloat(out);
+ writeCpLong(out);
+ writeCpDouble(out);
+ writeCpString(out);
+ writeCpClass(out);
+ writeCpSignature(out);
+ writeCpDescr(out);
+ writeCpMethodOrField(cp_Field, out, "cp_Field");
+ writeCpMethodOrField(cp_Method, out, "cp_Method");
+ writeCpMethodOrField(cp_Imethod, out, "cp_Imethod");
+ }
+
+ private void writeCpUtf8(OutputStream out) throws IOException,
+ Pack200Exception {
+ PackingUtils.log("Writing " + cp_Utf8.size() + " UTF8 entries...");
+ int[] cpUtf8Prefix = new int[cp_Utf8.size() - 2];
+ int[] cpUtf8Suffix = new int[cp_Utf8.size() - 1];
+ List chars = new ArrayList();
+ List bigSuffix = new ArrayList();
+ List bigChars = new ArrayList();
+ Object[] cpUtf8Array = cp_Utf8.toArray();
+ String first = ((CPUTF8) cpUtf8Array[1]).getUnderlyingString();
+ cpUtf8Suffix[0] = first.length();
+ addCharacters(chars, first.toCharArray());
+ for (int i = 2; i < cpUtf8Array.length; i++) {
+ char[] previous = ((CPUTF8) cpUtf8Array[i - 1])
+ .getUnderlyingString().toCharArray();
+ String currentStr = ((CPUTF8) cpUtf8Array[i]).getUnderlyingString();
+ char[] current = currentStr.toCharArray();
+ int prefix = 0;
+ for (int j = 0; j < previous.length; j++) {
+ if (previous[j] == current[j]) {
+ prefix++;
+ } else {
+ break;
+ }
+ }
+ cpUtf8Prefix[i - 2] = prefix;
+ currentStr = currentStr.substring(prefix);
+ char[] suffix = currentStr.toCharArray();
+ if (suffix.length > 1000) { // big suffix (1000 is arbitrary - can we
+ // do better?)
+ cpUtf8Suffix[i - 1] = 0;
+ bigSuffix.add(new Integer(suffix.length));
+ addCharacters(bigChars, suffix);
+ } else {
+ cpUtf8Suffix[i - 1] = suffix.length;
+ addCharacters(chars, suffix);
+ }
+ }
+ int[] cpUtf8Chars = new int[chars.size()];
+ int[] cpUtf8BigSuffix = new int[bigSuffix.size()];
+ int[][] cpUtf8BigChars = new int[bigSuffix.size()][];
+ for (int i = 0; i < cpUtf8Chars.length; i++) {
+ cpUtf8Chars[i] = ((Character) chars.get(i)).charValue();
+ }
+ for (int i = 0; i < cpUtf8BigSuffix.length; i++) {
+ int numBigChars = ((Integer) bigSuffix.get(i)).intValue();
+ cpUtf8BigSuffix[i] = numBigChars;
+ cpUtf8BigChars[i] = new int[numBigChars];
+ for (int j = 0; j < numBigChars; j++) {
+ cpUtf8BigChars[i][j] = ((Character) bigChars.remove(0)).charValue();
+ }
+ }
+
+ byte[] encodedBand = encodeBandInt("cpUtf8Prefix", cpUtf8Prefix, Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cpUtf8Prefix[" + cpUtf8Prefix.length + "]");
+
+ encodedBand = encodeBandInt("cpUtf8Suffix", cpUtf8Suffix, Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cpUtf8Suffix[" + cpUtf8Suffix.length + "]");
+
+ encodedBand = encodeBandInt("cpUtf8Chars", cpUtf8Chars, Codec.CHAR3);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cpUtf8Chars[" + cpUtf8Chars.length + "]");
+
+ encodedBand = encodeBandInt("cpUtf8BigSuffix", cpUtf8BigSuffix,
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cpUtf8BigSuffix[" + cpUtf8BigSuffix.length + "]");
+
+ for (int i = 0; i < cpUtf8BigChars.length; i++) {
+ encodedBand = encodeBandInt("cpUtf8BigChars " + i,
+ cpUtf8BigChars[i], Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cpUtf8BigChars" + i + "["
+ + cpUtf8BigChars[i].length + "]");
+ }
+ }
+
+ private void addCharacters(List chars, char[] charArray) {
+ for (int i = 0; i < charArray.length; i++) {
+ chars.add(new Character(charArray[i]));
+ }
+ }
+
+ private void writeCpInt(OutputStream out) throws IOException,
+ Pack200Exception {
+ PackingUtils.log("Writing " + cp_Int.size() + " Integer entries...");
+ int[] cpInt = new int[cp_Int.size()];
+ int i = 0;
+ for (Iterator iterator = cp_Int.iterator(); iterator.hasNext();) {
+ CPInt integer = (CPInt) iterator.next();
+ cpInt[i] = integer.getInt();
+ i++;
+ }
+ byte[] encodedBand = encodeBandInt("cp_Int", cpInt, Codec.UDELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cp_Int[" + cpInt.length + "]");
+ }
+
+ private void writeCpFloat(OutputStream out) throws IOException,
+ Pack200Exception {
+ PackingUtils.log("Writing " + cp_Float.size() + " Float entries...");
+ int[] cpFloat = new int[cp_Float.size()];
+ int i = 0;
+ for (Iterator iterator = cp_Float.iterator(); iterator.hasNext();) {
+ CPFloat fl = (CPFloat) iterator.next();
+ cpFloat[i] = Float.floatToIntBits(fl.getFloat());
+ i++;
+ }
+ byte[] encodedBand = encodeBandInt("cp_Float", cpFloat, Codec.UDELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cp_Float[" + cpFloat.length + "]");
+ }
+
+ private void writeCpLong(OutputStream out) throws IOException,
+ Pack200Exception {
+ PackingUtils.log("Writing " + cp_Long.size() + " Long entries...");
+ int[] highBits = new int[cp_Long.size()];
+ int[] loBits = new int[cp_Long.size()];
+ int i = 0;
+ for (Iterator iterator = cp_Long.iterator(); iterator.hasNext();) {
+ CPLong lng = (CPLong) iterator.next();
+ long l = lng.getLong();
+ highBits[i] = (int) (l >> 32);
+ loBits[i] = (int) l;
+ i++;
+ }
+ byte[] encodedBand = encodeBandInt("cp_Long_hi", highBits, Codec.UDELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cp_Long_hi[" + highBits.length + "]");
+
+ encodedBand = encodeBandInt("cp_Long_lo", loBits, Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cp_Long_lo[" + loBits.length + "]");
+ }
+
+ private void writeCpDouble(OutputStream out) throws IOException,
+ Pack200Exception {
+ PackingUtils.log("Writing " + cp_Double.size() + " Double entries...");
+ int[] highBits = new int[cp_Double.size()];
+ int[] loBits = new int[cp_Double.size()];
+ int i = 0;
+ for (Iterator iterator = cp_Double.iterator(); iterator.hasNext();) {
+ CPDouble dbl = (CPDouble) iterator.next();
+ long l = Double.doubleToLongBits(dbl.getDouble());
+ highBits[i] = (int) (l >> 32);
+ loBits[i] = (int) l;
+ i++;
+ }
+ byte[] encodedBand = encodeBandInt("cp_Double_hi", highBits, Codec.UDELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cp_Double_hi[" + highBits.length + "]");
+
+ encodedBand = encodeBandInt("cp_Double_lo", loBits, Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cp_Double_lo[" + loBits.length + "]");
+ }
+
+ private void writeCpString(OutputStream out) throws IOException,
+ Pack200Exception {
+ PackingUtils.log("Writing " + cp_String.size() + " String entries...");
+ int[] cpString = new int[cp_String.size()];
+ int i = 0;
+ for (Iterator iterator = cp_String.iterator(); iterator.hasNext();) {
+ CPString cpStr = (CPString) iterator.next();
+ cpString[i] = cpStr.getIndexInCpUtf8();
+ i++;
+ }
+ byte[] encodedBand = encodeBandInt("cpString", cpString, Codec.UDELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cpString[" + cpString.length + "]");
+ }
+
+ private void writeCpClass(OutputStream out) throws IOException,
+ Pack200Exception {
+ PackingUtils.log("Writing " + cp_Class.size() + " Class entries...");
+ int[] cpClass = new int[cp_Class.size()];
+ int i = 0;
+ for (Iterator iterator = cp_Class.iterator(); iterator.hasNext();) {
+ CPClass cpCl = (CPClass) iterator.next();
+ cpClass[i] = cpCl.getIndexInCpUtf8();
+ i++;
+ }
+ byte[] encodedBand = encodeBandInt("cpClass", cpClass, Codec.UDELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cpClass[" + cpClass.length + "]");
+ }
+
+ private void writeCpSignature(OutputStream out) throws IOException,
+ Pack200Exception {
+ PackingUtils.log("Writing " + cp_Signature.size() + " Signature entries...");
+ int[] cpSignatureForm = new int[cp_Signature.size()];
+ List classes = new ArrayList();
+ int i = 0;
+ for (Iterator iterator = cp_Signature.iterator(); iterator.hasNext();) {
+ CPSignature cpS = (CPSignature) iterator.next();
+ classes.addAll(cpS.getClasses());
+ cpSignatureForm[i] = cpS.getIndexInCpUtf8();
+ i++;
+ }
+ int[] cpSignatureClasses = new int[classes.size()];
+ for (int j = 0; j < cpSignatureClasses.length; j++) {
+ cpSignatureClasses[j] = ((CPClass) classes.get(j)).getIndex();
+ }
+
+ byte[] encodedBand = encodeBandInt("cpSignatureForm", cpSignatureForm,
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cpSignatureForm[" + cpSignatureForm.length + "]");
+
+ encodedBand = encodeBandInt("cpSignatureClasses", cpSignatureClasses,
+ Codec.UDELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cpSignatureClasses[" + cpSignatureClasses.length + "]");
+ }
+
+ private void writeCpDescr(OutputStream out) throws IOException,
+ Pack200Exception {
+ PackingUtils.log("Writing " + cp_Descr.size()
+ + " Descriptor entries...");
+ int[] cpDescrName = new int[cp_Descr.size()];
+ int[] cpDescrType = new int[cp_Descr.size()];
+ int i = 0;
+ for (Iterator iterator = cp_Descr.iterator(); iterator.hasNext();) {
+ CPNameAndType nameAndType = (CPNameAndType) iterator.next();
+ cpDescrName[i] = nameAndType.getNameIndex();
+ cpDescrType[i] = nameAndType.getTypeIndex();
+ i++;
+ }
+
+ byte[] encodedBand = encodeBandInt("cp_Descr_Name", cpDescrName,
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cp_Descr_Name[" + cpDescrName.length + "]");
+
+ encodedBand = encodeBandInt("cp_Descr_Type", cpDescrType, Codec.UDELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from cp_Descr_Type[" + cpDescrType.length + "]");
+ }
+
+ private void writeCpMethodOrField(Set cp, OutputStream out, String name)
+ throws IOException, Pack200Exception {
+ PackingUtils.log("Writing " + cp.size()
+ + " Method and Field entries...");
+ int[] cp_methodOrField_class = new int[cp.size()];
+ int[] cp_methodOrField_desc = new int[cp.size()];
+ int i = 0;
+ for (Iterator iterator = cp.iterator(); iterator.hasNext();) {
+ CPMethodOrField mOrF = (CPMethodOrField) iterator.next();
+ cp_methodOrField_class[i] = mOrF.getClassIndex();
+ cp_methodOrField_desc[i] = mOrF.getDescIndex();
+ i++;
+ }
+ byte[] encodedBand = encodeBandInt(name + "_class",
+ cp_methodOrField_class, Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + name + "_class[" + cp_methodOrField_class.length + "]");
+
+ encodedBand = encodeBandInt(name + "_desc", cp_methodOrField_desc,
+ Codec.UDELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + name + "_desc[" + cp_methodOrField_desc.length + "]");
+ }
+
+ /**
+ * All input classes for the segment have now been read in, so this method
+ * is called so that this class can calculate/complete anything it could not
+ * do while classes were being read.
+ */
+ public void finaliseBands() {
+ addCPUtf8("");
+ removeSignaturesFromCpUTF8();
+ addIndices();
+ segmentHeader.setCp_Utf8_count(cp_Utf8.size());
+ segmentHeader.setCp_Int_count(cp_Int.size());
+ segmentHeader.setCp_Float_count(cp_Float.size());
+ segmentHeader.setCp_Long_count(cp_Long.size());
+ segmentHeader.setCp_Double_count(cp_Double.size());
+ segmentHeader.setCp_String_count(cp_String.size());
+ segmentHeader.setCp_Class_count(cp_Class.size());
+ segmentHeader.setCp_Signature_count(cp_Signature.size());
+ segmentHeader.setCp_Descr_count(cp_Descr.size());
+ segmentHeader.setCp_Field_count(cp_Field.size());
+ segmentHeader.setCp_Method_count(cp_Method.size());
+ segmentHeader.setCp_Imethod_count(cp_Imethod.size());
+ }
+
+ private void removeSignaturesFromCpUTF8() {
+ for (Iterator iterator = cp_Signature.iterator(); iterator.hasNext();) {
+ CPSignature signature = (CPSignature) iterator.next();
+ String sigStr = signature.getUnderlyingString();
+ CPUTF8 utf8 = signature.getSignatureForm();
+ String form = utf8.getUnderlyingString();
+ if (!sigStr.equals(form)) {
+ removeCpUtf8(sigStr);
+ }
+ }
+ }
+
+ private void addIndices() {
+ Set[] sets = new Set[] { cp_Utf8, cp_Int, cp_Float, cp_Long, cp_Double,
+ cp_String, cp_Class, cp_Signature, cp_Descr, cp_Field,
+ cp_Method, cp_Imethod };
+ for (int i = 0; i < sets.length; i++) {
+ int j = 0;
+ for (Iterator iterator = sets[i].iterator(); iterator.hasNext();) {
+ ConstantPoolEntry entry = (ConstantPoolEntry) iterator.next();
+ entry.setIndex(j);
+ j++;
+ }
+ }
+ Map classNameToIndex = new HashMap();
+ for (Iterator iterator = cp_Field.iterator(); iterator.hasNext();) {
+ CPMethodOrField mOrF = (CPMethodOrField) iterator.next();
+ CPClass className = mOrF.getClassName();
+ Integer index = (Integer) classNameToIndex.get(className);
+ if (index == null) {
+ classNameToIndex.put(className, new Integer(1));
+ mOrF.setIndexInClass(0);
+ } else {
+ int theIndex = index.intValue();
+ mOrF.setIndexInClass(theIndex);
+ classNameToIndex.put(className, new Integer(theIndex + 1));
+ }
+ }
+ classNameToIndex.clear();
+ Map classNameToConstructorIndex = new HashMap();
+ for (Iterator iterator = cp_Method.iterator(); iterator.hasNext();) {
+ CPMethodOrField mOrF = (CPMethodOrField) iterator.next();
+ CPClass className = mOrF.getClassName();
+ Integer index = (Integer) classNameToIndex.get(className);
+ if (index == null) {
+ classNameToIndex.put(className, new Integer(1));
+ mOrF.setIndexInClass(0);
+ } else {
+ int theIndex = index.intValue();
+ mOrF.setIndexInClass(theIndex);
+ classNameToIndex.put(className, new Integer(theIndex + 1));
+ }
+ if(mOrF.getDesc().getName().equals("<init>")) {
+ Integer constructorIndex = (Integer) classNameToConstructorIndex.get(className);
+ if (constructorIndex == null) {
+ classNameToConstructorIndex.put(className, new Integer(1));
+ mOrF.setIndexInClassForConstructor(0);
+ } else {
+ int theIndex = constructorIndex.intValue();
+ mOrF.setIndexInClassForConstructor(theIndex);
+ classNameToConstructorIndex.put(className, new Integer(theIndex + 1));
+ }
+ }
+ }
+ }
+
+ private void removeCpUtf8(String string) {
+ CPUTF8 utf8 = (CPUTF8) stringsToCpUtf8.get(string);
+ if (utf8 != null) {
+ if(stringsToCpClass.get(string) == null) { // don't remove if strings are also in cpclass
+ stringsToCpUtf8.remove(string);
+ cp_Utf8.remove(utf8);
+ }
+ }
+ }
+
+ void addCPUtf8(String utf8) {
+ getCPUtf8(utf8);
+ }
+
+ public CPUTF8 getCPUtf8(String utf8) {
+ if(utf8 == null) {
+ return null;
+ }
+ CPUTF8 cpUtf8 = (CPUTF8) stringsToCpUtf8.get(utf8);
+ if (cpUtf8 == null) {
+ cpUtf8 = new CPUTF8(utf8);
+ cp_Utf8.add(cpUtf8);
+ stringsToCpUtf8.put(utf8, cpUtf8);
+ }
+ return cpUtf8;
+ }
+
+ public CPSignature getCPSignature(String signature) {
+ if(signature == null) {
+ return null;
+ }
+ CPSignature cpS = (CPSignature) stringsToCpSignature.get(signature);
+ if (cpS == null) {
+ List cpClasses = new ArrayList();
+ CPUTF8 signatureUTF8;
+ if (signature.length() > 1 && signature.indexOf('L') != -1) {
+ List classes = new ArrayList();
+ char[] chars = signature.toCharArray();
+ StringBuffer signatureString = new StringBuffer();
+ for (int i = 0; i < chars.length; i++) {
+ signatureString.append(chars[i]);
+ if (chars[i] == 'L') {
+ StringBuffer className = new StringBuffer();
+ for (int j = i + 1; j < chars.length; j++) {
+ char c = chars[j];
+ if (Character.isLetter(c) || Character.isDigit(c)
+ || c == '/' || c == '$' || c == '_') {
+ className.append(c);
+ } else {
+ classes.add(className.toString());
+ i = j - 1;
+ break;
+ }
+ }
+ }
+ }
+ removeCpUtf8(signature);
+ for (Iterator iterator2 = classes.iterator(); iterator2
+ .hasNext();) {
+ String className = (String) iterator2.next();
+ CPClass cpClass = null;
+ if(className!= null) {
+ className = className.replace('.', '/');
+ cpClass = (CPClass) stringsToCpClass.get(className);
+ if (cpClass == null) {
+ CPUTF8 cpUtf8 = getCPUtf8(className);
+ cpClass = new CPClass(cpUtf8);
+ cp_Class.add(cpClass);
+ stringsToCpClass.put(className, cpClass);
+ }
+ }
+ cpClasses.add(cpClass);
+ }
+
+ signatureUTF8 = getCPUtf8(signatureString.toString());
+ } else {
+ signatureUTF8 = getCPUtf8(signature);
+ }
+ cpS = new CPSignature(signature, signatureUTF8, cpClasses);
+ cp_Signature.add(cpS);
+ stringsToCpSignature.put(signature, cpS);
+ }
+ return cpS;
+ }
+
+ public CPClass getCPClass(String className) {
+ if(className == null) {
+ return null;
+ }
+ className = className.replace('.', '/');
+ CPClass cpClass = (CPClass) stringsToCpClass.get(className);
+ if (cpClass == null) {
+ CPUTF8 cpUtf8 = getCPUtf8(className);
+ cpClass = new CPClass(cpUtf8);
+ cp_Class.add(cpClass);
+ stringsToCpClass.put(className, cpClass);
+ }
+ if(cpClass.isInnerClass()) {
+ segment.getClassBands().currentClassReferencesInnerClass(cpClass);
+ }
+ return cpClass;
+ }
+
+ public void addCPClass(String className) {
+ getCPClass(className);
+ }
+
+ public CPNameAndType getCPNameAndType(String name, String signature) {
+ String descr = name + ":" + signature;
+ CPNameAndType nameAndType = (CPNameAndType) stringsToCpNameAndType
+ .get(descr);
+ if (nameAndType == null) {
+ nameAndType = new CPNameAndType(getCPUtf8(name),
+ getCPSignature(signature));
+ stringsToCpNameAndType.put(descr, nameAndType);
+ cp_Descr.add(nameAndType);
+ }
+ return nameAndType;
+ }
+
+ public CPMethodOrField getCPField(CPClass cpClass, String name, String desc) {
+ String key = cpClass.toString() + ":" + name + ":" + desc;
+ CPMethodOrField cpF = (CPMethodOrField) stringsToCpField
+ .get(key);
+ if (cpF == null) {
+ CPNameAndType nAndT = getCPNameAndType(name, desc);
+ cpF = new CPMethodOrField(cpClass, nAndT);
+ cp_Field.add(cpF);
+ stringsToCpField.put(key, cpF);
+ }
+ return cpF;
+ }
+
+ public CPConstant getConstant(Object value) {
+ CPConstant constant = (CPConstant) objectsToCPConstant.get(value);
+ if (constant == null) {
+ if (value instanceof Integer) {
+ constant = new CPInt(((Integer) value).intValue());
+ cp_Int.add(constant);
+ } else if (value instanceof Long) {
+ constant = new CPLong(((Long) value).longValue());
+ cp_Long.add(constant);
+ } else if (value instanceof Float) {
+ constant = new CPFloat(((Float) value).floatValue());
+ cp_Float.add(constant);
+ } else if (value instanceof Double) {
+ constant = new CPDouble(((Double) value).doubleValue());
+ cp_Double.add(constant);
+ } else if (value instanceof String) {
+ constant = new CPString(getCPUtf8((String) value));
+ cp_String.add(constant);
+ } else if (value instanceof Type) {
+ String className = ((Type) value).getClassName();
+ if(className.endsWith("[]")) {
+ className = "[L" + className.substring(0, className.length() - 2);
+ while(className.endsWith("[]")) {
+ className = "[" + className.substring(0, className.length() - 2);
+ }
+ className += ";";
+ }
+ constant = getCPClass(className);
+ }
+ objectsToCPConstant.put(value, constant);
+ }
+ return constant;
+ }
+
+ public CPMethodOrField getCPMethod(CPClass cpClass, String name, String desc) {
+ String key = cpClass.toString() + ":" + name + ":" + desc;
+ CPMethodOrField cpM = (CPMethodOrField) stringsToCpMethod
+ .get(key);
+ if (cpM == null) {
+ CPNameAndType nAndT = getCPNameAndType(name, desc);
+ cpM = new CPMethodOrField(cpClass, nAndT);
+ cp_Method.add(cpM);
+ stringsToCpMethod.put(key, cpM);
+ }
+ return cpM;
+ }
+
+ public CPMethodOrField getCPIMethod(CPClass cpClass, String name,
+ String desc) {
+ String key = cpClass.toString() + ":" + name + ":" + desc;
+ CPMethodOrField cpIM = (CPMethodOrField) stringsToCpIMethod
+ .get(key);
+ if (cpIM == null) {
+ CPNameAndType nAndT = getCPNameAndType(name, desc);
+ cpIM = new CPMethodOrField(cpClass, nAndT);
+ cp_Imethod.add(cpIM);
+ stringsToCpIMethod.put(key, cpIM);
+ }
+ return cpIM;
+ }
+
+ public CPMethodOrField getCPField(String owner, String name, String desc) {
+ return getCPField(getCPClass(owner), name, desc);
+ }
+
+ public CPMethodOrField getCPMethod(String owner, String name, String desc) {
+ return getCPMethod(getCPClass(owner), name, desc);
+ }
+
+ public CPMethodOrField getCPIMethod(String owner, String name, String desc) {
+ return getCPIMethod(getCPClass(owner), name, desc);
+ }
+
+ public boolean existsCpClass(String className) {
+ CPClass cpClass = (CPClass) stringsToCpClass.get(className);
+ return cpClass != null;
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/FileBands.java b/src/main/java/org/apache/harmony/pack200/FileBands.java
new file mode 100644
index 0000000..be9cc00
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/FileBands.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.TimeZone;
+
+import org.apache.harmony.pack200.Archive.PackingFile;
+import org.apache.harmony.pack200.Archive.SegmentUnit;
+import org.objectweb.asm.ClassReader;
+
+/**
+ * Bands containing information about files in the pack200 archive and the file
+ * contents for non-class-files. Corresponds to the <code>file_bands</code> set
+ * of bands described in the specification.
+ */
+public class FileBands extends BandSet {
+
+ private final CPUTF8[] fileName;
+ private int[] file_name;
+ private final int[] file_modtime;
+ private final long[] file_size;
+ private final int[] file_options;
+ private final byte[][] file_bits;
+ private final List fileList;
+ private final PackingOptions options;
+ private final CpBands cpBands;
+
+ public FileBands(CpBands cpBands, SegmentHeader segmentHeader,
+ PackingOptions options, SegmentUnit segmentUnit, int effort) {
+ super(effort, segmentHeader);
+ fileList = segmentUnit.getFileList();
+ this.options = options;
+ this.cpBands = cpBands;
+ int size = fileList.size();
+ fileName = new CPUTF8[size];
+ file_modtime = new int[size];
+ file_size = new long[size];
+ file_options = new int[size];
+ int totalSize = 0;
+ file_bits = new byte[size][];
+ int archiveModtime = segmentHeader.getArchive_modtime();
+
+ Set classNames = new HashSet();
+ for (Iterator iterator = segmentUnit.getClassList().iterator(); iterator
+ .hasNext();) {
+ ClassReader reader = (ClassReader) iterator.next();
+ classNames.add(reader.getClassName());
+ }
+ CPUTF8 emptyString = cpBands.getCPUtf8("");
+ long modtime;
+ int latestModtime = Integer.MIN_VALUE;
+ boolean isLatest = !PackingOptions.KEEP.equals(options
+ .getModificationTime());
+ for (int i = 0; i < size; i++) {
+ PackingFile packingFile = (PackingFile) fileList.get(i);
+ String name = packingFile.getName();
+ if (name.endsWith(".class") && !options.isPassFile(name)) {
+ file_options[i] |= (1 << 1);
+ if (classNames.contains(name.substring(0, name.length() - 6))) {
+ fileName[i] = emptyString;
+ } else {
+ fileName[i] = cpBands.getCPUtf8(name);
+ }
+ } else {
+ fileName[i] = cpBands.getCPUtf8(name);
+ }
+ // set deflate_hint for file element
+ if (options.isKeepDeflateHint() && packingFile.isDefalteHint()) {
+ file_options[i] |= 0x1;
+ }
+ byte[] bytes = packingFile.getContents();
+ file_size[i] = bytes.length;
+ totalSize += file_size[i];
+
+ // update modification time
+ modtime = (packingFile.getModtime() + TimeZone.getDefault().getRawOffset()) / 1000L;
+ file_modtime[i] = (int) (modtime - archiveModtime);
+ if (isLatest && latestModtime < file_modtime[i]) {
+ latestModtime = file_modtime[i];
+ }
+
+ file_bits[i] = packingFile.getContents();
+ }
+
+ if (isLatest) {
+ for (int index = 0; index < size; index++) {
+ file_modtime[index] = latestModtime;
+ }
+ }
+ }
+
+ /**
+ * All input classes for the segment have now been read in, so this method
+ * is called so that this class can calculate/complete anything it could not
+ * do while classes were being read.
+ */
+ public void finaliseBands() {
+ file_name = new int[fileName.length];
+ for (int i = 0; i < file_name.length; i++) {
+ if (fileName[i].equals(cpBands.getCPUtf8(""))) {
+ PackingFile packingFile = (PackingFile) fileList.get(i);
+ String name = packingFile.getName();
+ if (options.isPassFile(name)) {
+ fileName[i] = cpBands.getCPUtf8(name);
+ file_options[i] &= (1 << 1) ^ 0xFFFFFFFF;
+ }
+ }
+ file_name[i] = fileName[i].getIndex();
+ }
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ PackingUtils.log("Writing file bands...");
+ byte[] encodedBand = encodeBandInt("file_name", file_name,
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from file_name[" + file_name.length + "]");
+
+ encodedBand = encodeFlags("file_size", file_size, Codec.UNSIGNED5,
+ Codec.UNSIGNED5, segmentHeader.have_file_size_hi());
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from file_size[" + file_size.length + "]");
+
+ if (segmentHeader.have_file_modtime()) {
+ encodedBand = encodeBandInt("file_modtime", file_modtime,
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from file_modtime[" + file_modtime.length + "]");
+ }
+ if (segmentHeader.have_file_options()) {
+ encodedBand = encodeBandInt("file_options", file_options,
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from file_options[" + file_options.length + "]");
+ }
+
+ encodedBand = encodeBandInt("file_bits", flatten(file_bits),
+ Codec.BYTE1);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from file_bits[" + file_bits.length + "]");
+ }
+
+ private int[] flatten(byte[][] bytes) {
+ int total = 0;
+ for (int i = 0; i < bytes.length; i++) {
+ total += bytes[i].length;
+ }
+ int[] band = new int[total];
+ int index = 0;
+ for (int i = 0; i < bytes.length; i++) {
+ for (int j = 0; j < bytes[i].length; j++) {
+ band[index++] = bytes[i][j] & 0xFF;
+ }
+ }
+ return band;
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/IcBands.java b/src/main/java/org/apache/harmony/pack200/IcBands.java
new file mode 100644
index 0000000..8eac217
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/IcBands.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Inner class bands (corresponds to the <code>ic_bands</code> set of bands in
+ * the pack200 specification)
+ */
+public class IcBands extends BandSet {
+
+ private final Set innerClasses = new TreeSet();
+ private final CpBands cpBands;
+ private int bit16Count = 0;
+
+ private final Map outerToInner = new HashMap();
+
+ public IcBands(SegmentHeader segmentHeader, CpBands cpBands, int effort) {
+ super(effort, segmentHeader);
+ this.cpBands = cpBands;
+ }
+
+ /**
+ * All input classes for the segment have now been read in, so this method
+ * is called so that this class can calculate/complete anything it could not
+ * do while classes were being read.
+ */
+ public void finaliseBands() {
+ segmentHeader.setIc_count(innerClasses.size());
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ PackingUtils.log("Writing internal class bands...");
+ int[] ic_this_class = new int[innerClasses.size()];
+ int[] ic_flags = new int[innerClasses.size()];
+ int[] ic_outer_class = new int[bit16Count];
+ int[] ic_name = new int[bit16Count];
+
+ int index2 = 0;
+ List innerClassesList = new ArrayList(innerClasses);
+ for (int i = 0; i < ic_this_class.length; i++) {
+ IcTuple icTuple = (IcTuple) innerClassesList.get(i);
+ ic_this_class[i] = icTuple.C.getIndex();
+ ic_flags[i] = icTuple.F;
+ if((icTuple.F & (1<<16)) != 0) {
+ ic_outer_class[index2] = icTuple.C2 == null ? 0 : icTuple.C2.getIndex() + 1;
+ ic_name[index2] = icTuple.N == null ? 0 : icTuple.N.getIndex() + 1;
+ index2++;
+ }
+ }
+ byte[] encodedBand = encodeBandInt("ic_this_class", ic_this_class,
+ Codec.UDELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from ic_this_class[" + ic_this_class.length + "]");
+
+ encodedBand = encodeBandInt("ic_flags", ic_flags, Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from ic_flags[" + ic_flags.length + "]");
+
+ encodedBand = encodeBandInt("ic_outer_class", ic_outer_class,
+ Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from ic_outer_class[" + ic_outer_class.length + "]");
+
+ encodedBand = encodeBandInt("ic_name", ic_name, Codec.DELTA5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from ic_name[" + ic_name.length + "]");
+ }
+
+ public void addInnerClass(String name, String outerName, String innerName,
+ int flags) {
+ if(outerName != null || innerName != null) {
+ if(namesArePredictable(name, outerName, innerName)) {
+ IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null);
+ addToMap(outerName, innerClass);
+ innerClasses.add(innerClass);
+ } else {
+ flags |= (1<<16);
+ IcTuple icTuple = new IcTuple(cpBands.getCPClass(name), flags, cpBands.getCPClass(outerName), cpBands.getCPUtf8(innerName));
+ boolean added = innerClasses.add(icTuple);
+ if(added) {
+ bit16Count++;
+ addToMap(outerName, icTuple);
+ }
+ }
+ } else {
+ IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null);
+ addToMap(getOuter(name), innerClass);
+ innerClasses.add(innerClass);
+ }
+ }
+
+ public List getInnerClassesForOuter(String outerClassName) {
+ return (List) outerToInner.get(outerClassName);
+ }
+
+ private String getOuter(String name) {
+ return name.substring(0, name.lastIndexOf('$'));
+ }
+
+ private void addToMap(String outerName, IcTuple icTuple) {
+ List tuples = (List) outerToInner.get(outerName);
+ if(tuples == null) {
+ tuples = new ArrayList();
+ outerToInner.put(outerName, tuples);
+ tuples.add(icTuple);
+ } else {
+ for (Iterator iterator = tuples.iterator(); iterator.hasNext();) {
+ IcTuple icT = (IcTuple) iterator.next();
+ if(icTuple.equals(icT)) {
+ return;
+ }
+ }
+ tuples.add(icTuple);
+ }
+ }
+
+ private boolean namesArePredictable(String name, String outerName,
+ String innerName) {
+ // TODO: Could be multiple characters, not just $
+ return name.equals(outerName + '$' + innerName) && innerName.indexOf('$') == -1;
+ }
+
+ class IcTuple implements Comparable {
+
+ protected CPClass C; // this class
+ protected int F; // flags
+ protected CPClass C2; // outer class
+ protected CPUTF8 N; // name
+
+ public IcTuple(CPClass C, int F, CPClass C2, CPUTF8 N) {
+ this.C = C;
+ this.F = F;
+ this.C2 = C2;
+ this.N = N;
+ }
+
+ public boolean equals(Object o) {
+ if(o instanceof IcTuple) {
+ IcTuple icT = (IcTuple)o;
+ return C.equals(icT.C) && F == icT.F && (C2 != null ? C2.equals(icT.C2) : icT.C2 == null) && (N != null ? N.equals(icT.N) : icT.N == null);
+ }
+ return false;
+ }
+
+ public String toString() {
+ return C.toString();
+ }
+
+ public int compareTo(Object arg0) {
+ return C.compareTo(((IcTuple)arg0).C);
+ }
+
+ public boolean isAnonymous() {
+ String className = C.toString();
+ String innerName = className.substring(className.lastIndexOf('$') + 1);
+ return Character.isDigit(innerName.charAt(0));
+ }
+
+ }
+
+ public IcTuple getIcTuple(CPClass inner) {
+ for (Iterator iterator = innerClasses.iterator(); iterator.hasNext();) {
+ IcTuple icTuple = (IcTuple) iterator.next();
+ if(icTuple.C.equals(inner)) {
+ return icTuple;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/IntList.java b/src/main/java/org/apache/harmony/pack200/IntList.java
new file mode 100644
index 0000000..efa8e1c
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/IntList.java
@@ -0,0 +1,260 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.util.Arrays;
+
+/**
+ * IntList is based on <code>java.util.ArrayList</code>, but is written
+ * specifically for ints in order to reduce boxing and unboxing to Integers,
+ * reduce the memory required and improve performance of pack200.
+ */
+public class IntList {
+
+ private int[] array;
+ private int firstIndex;
+ private int lastIndex;
+ private int modCount;
+
+ /**
+ * Constructs a new instance of IntList with capacity for ten elements.
+ */
+ public IntList() {
+ this(10);
+ }
+
+ /**
+ * Constructs a new instance of IntList with the specified capacity.
+ *
+ * @param capacity
+ * the initial capacity of this IntList
+ */
+ public IntList(int capacity) {
+ if (capacity < 0) {
+ throw new IllegalArgumentException();
+ }
+ firstIndex = lastIndex = 0;
+ array = new int[capacity];
+ }
+
+ /**
+ * Adds the specified object at the end of this IntList.
+ *
+ * @param object
+ * the object to add
+ * @return true
+ */
+ public boolean add(int object) {
+ if (lastIndex == array.length) {
+ growAtEnd(1);
+ }
+ array[lastIndex++] = object;
+ modCount++;
+ return true;
+ }
+
+ public void add(int location, int object) {
+ int size = lastIndex - firstIndex;
+ if (0 < location && location < size) {
+ if (firstIndex == 0 && lastIndex == array.length) {
+ growForInsert(location, 1);
+ } else if ((location < size / 2 && firstIndex > 0)
+ || lastIndex == array.length) {
+ System.arraycopy(array, firstIndex, array, --firstIndex,
+ location);
+ } else {
+ int index = location + firstIndex;
+ System.arraycopy(array, index, array, index + 1, size
+ - location);
+ lastIndex++;
+ }
+ array[location + firstIndex] = object;
+ } else if (location == 0) {
+ if (firstIndex == 0) {
+ growAtFront(1);
+ }
+ array[--firstIndex] = object;
+ } else if (location == size) {
+ if (lastIndex == array.length) {
+ growAtEnd(1);
+ }
+ array[lastIndex++] = object;
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+
+ modCount++;
+ }
+
+ public void clear() {
+ if (firstIndex != lastIndex) {
+ Arrays.fill(array, firstIndex, lastIndex, -1);
+ firstIndex = lastIndex = 0;
+ modCount++;
+ }
+ }
+
+ public int get(int location) {
+ if (0 <= location && location < (lastIndex - firstIndex)) {
+ return array[firstIndex + location];
+ }
+ throw new IndexOutOfBoundsException("" + location);
+ }
+
+ private void growAtEnd(int required) {
+ int size = lastIndex - firstIndex;
+ if (firstIndex >= required - (array.length - lastIndex)) {
+ int newLast = lastIndex - firstIndex;
+ if (size > 0) {
+ System.arraycopy(array, firstIndex, array, 0, size);
+ }
+ firstIndex = 0;
+ lastIndex = newLast;
+ } else {
+ int increment = size / 2;
+ if (required > increment) {
+ increment = required;
+ }
+ if (increment < 12) {
+ increment = 12;
+ }
+ int[] newArray = new int[size + increment];
+ if (size > 0) {
+ System.arraycopy(array, firstIndex, newArray, 0, size);
+ firstIndex = 0;
+ lastIndex = size;
+ }
+ array = newArray;
+ }
+ }
+
+ private void growAtFront(int required) {
+ int size = lastIndex - firstIndex;
+ if (array.length - lastIndex + firstIndex >= required) {
+ int newFirst = array.length - size;
+ if (size > 0) {
+ System.arraycopy(array, firstIndex, array, newFirst, size);
+ }
+ firstIndex = newFirst;
+ lastIndex = array.length;
+ } else {
+ int increment = size / 2;
+ if (required > increment) {
+ increment = required;
+ }
+ if (increment < 12) {
+ increment = 12;
+ }
+ int[] newArray = new int[size + increment];
+ if (size > 0) {
+ System.arraycopy(array, firstIndex, newArray, newArray.length
+ - size, size);
+ }
+ firstIndex = newArray.length - size;
+ lastIndex = newArray.length;
+ array = newArray;
+ }
+ }
+
+ private void growForInsert(int location, int required) {
+ int size = lastIndex - firstIndex;
+ int increment = size / 2;
+ if (required > increment) {
+ increment = required;
+ }
+ if (increment < 12) {
+ increment = 12;
+ }
+ int[] newArray = new int[size + increment];
+ int newFirst = increment - required;
+ // Copy elements after location to the new array skipping inserted
+ // elements
+ System.arraycopy(array, location + firstIndex, newArray, newFirst
+ + location + required, size - location);
+ // Copy elements before location to the new array from firstIndex
+ System.arraycopy(array, firstIndex, newArray, newFirst, location);
+ firstIndex = newFirst;
+ lastIndex = size + increment;
+
+ array = newArray;
+ }
+
+ public void increment(int location) {
+ if (0 <= location && location < (lastIndex - firstIndex)) {
+ array[firstIndex + location]++;
+ } else {
+ throw new IndexOutOfBoundsException("" + location);
+ }
+ }
+
+ public boolean isEmpty() {
+ return lastIndex == firstIndex;
+ }
+
+ public int remove(int location) {
+ int result;
+ int size = lastIndex - firstIndex;
+ if (0 <= location && location < size) {
+ if (location == size - 1) {
+ result = array[--lastIndex];
+ array[lastIndex] = 0;
+ } else if (location == 0) {
+ result = array[firstIndex];
+ array[firstIndex++] = 0;
+ } else {
+ int elementIndex = firstIndex + location;
+ result = array[elementIndex];
+ if (location < size / 2) {
+ System.arraycopy(array, firstIndex, array, firstIndex + 1,
+ location);
+ array[firstIndex++] = 0;
+ } else {
+ System.arraycopy(array, elementIndex + 1, array,
+ elementIndex, size - location - 1);
+ array[--lastIndex] = 0;
+ }
+ }
+ if (firstIndex == lastIndex) {
+ firstIndex = lastIndex = 0;
+ }
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+
+ modCount++;
+ return result;
+ }
+
+ public int size() {
+ return lastIndex - firstIndex;
+ }
+
+ public int[] toArray() {
+ int size = lastIndex - firstIndex;
+ int[] result = new int[size];
+ System.arraycopy(array, firstIndex, result, 0, size);
+ return result;
+ }
+
+ public void addAll(IntList list) {
+ growAtEnd(list.size());
+ for (int i = 0; i < list.size(); i++) {
+ add(list.get(i));
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/MetadataBandGroup.java b/src/main/java/org/apache/harmony/pack200/MetadataBandGroup.java
new file mode 100644
index 0000000..1719396
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/MetadataBandGroup.java
@@ -0,0 +1,462 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You 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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A group of metadata (annotation) bands, such as class_RVA_bands,
+ * method_AD_bands etc.
+ */
+public class MetadataBandGroup extends BandSet {
+
+ public static final int CONTEXT_CLASS = 0;
+ public static final int CONTEXT_FIELD = 1;
+ public static final int CONTEXT_METHOD = 2;
+
+ private final String type;
+ private int numBackwardsCalls = 0;
+
+ public IntList param_NB = new IntList(); // TODO: Lazy instantiation?
+ public IntList anno_N = new IntList();
+ public List type_RS = new ArrayList();
+ public IntList pair_N = new IntList();
+ public List name_RU = new ArrayList();
+ public List T = new ArrayList();
+ public List caseI_KI = new ArrayList();
+ public List caseD_KD = new ArrayList();
+ public List caseF_KF = new ArrayList();
+ public List caseJ_KJ = new ArrayList();
+ public List casec_RS = new ArrayList();
+ public List caseet_RS = new ArrayList();
+ public List caseec_RU = new ArrayList();
+ public List cases_RU = new ArrayList();
+ public IntList casearray_N = new IntList();
+ public List nesttype_RS = new ArrayList();
+ public IntList nestpair_N = new IntList();
+ public List nestname_RU = new ArrayList();
+
+ private final CpBands cpBands;
+ private final int context;
+
+ /**
+ * Constructs a new MetadataBandGroup
+ *
+ * @param type
+ * must be either AD, RVA, RIA, RVPA or RIPA.
+ * @param context
+ * <code>CONTEXT_CLASS</code>, <code>CONTEXT_METHOD</code> or
+ * <code>CONTEXT_FIELD</code>
+ * @param cpBands
+ * constant pool bands
+ * @param segmentHeader
+ * segment header
+ * @param effort
+ * packing effort
+ */
+ public MetadataBandGroup(String type, int context, CpBands cpBands, SegmentHeader segmentHeader, int effort) {
+ super(effort, segmentHeader);
+ this.type = type;
+ this.cpBands = cpBands;
+ this.context = context;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.harmony.pack200.BandSet#pack(java.io.OutputStream)
+ */
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ PackingUtils.log("Writing metadata band group...");
+ if(hasContent()) {
+ String contextStr;
+ if(context == CONTEXT_CLASS) {
+ contextStr = "Class";
+ } else if (context == CONTEXT_FIELD) {
+ contextStr = "Field";
+ } else {
+ contextStr = "Method";
+ }
+ byte[] encodedBand = null;
+ if(!type.equals("AD")) {
+ if(type.indexOf('P') != -1) {
+ // Parameter annotation so we need to transmit param_NB
+ encodedBand = encodeBandInt(
+ contextStr + "_" + type + " param_NB", param_NB.toArray(),
+ Codec.BYTE1);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from " + contextStr + "_" + type + " anno_N["
+ + param_NB.size() + "]");
+ }
+ encodedBand = encodeBandInt(
+ contextStr + "_" + type + " anno_N", anno_N.toArray(),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from " + contextStr + "_" + type + " anno_N["
+ + anno_N.size() + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type
+ + " type_RS", cpEntryListToArray(type_RS),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from " + contextStr + "_" + type
+ + " type_RS[" + type_RS.size() + "]");
+
+ encodedBand = encodeBandInt(
+ contextStr + "_" + type + " pair_N", pair_N.toArray(),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from " + contextStr + "_" + type + " pair_N["
+ + pair_N.size() + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type
+ + " name_RU", cpEntryListToArray(name_RU),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length
+ + " bytes from " + contextStr + "_" + type
+ + " name_RU[" + name_RU.size() + "]");
+ }
+ encodedBand = encodeBandInt(contextStr + "_" + type + " T",
+ tagListToArray(T), Codec.BYTE1);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " T[" + T.size() + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type + " caseI_KI",
+ cpEntryListToArray(caseI_KI), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " caseI_KI[" + caseI_KI.size()
+ + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type + " caseD_KD",
+ cpEntryListToArray(caseD_KD), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " caseD_KD[" + caseD_KD.size()
+ + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type + " caseF_KF",
+ cpEntryListToArray(caseF_KF), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " caseF_KF[" + caseF_KF.size()
+ + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type + " caseJ_KJ",
+ cpEntryListToArray(caseJ_KJ), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " caseJ_KJ[" + caseJ_KJ.size()
+ + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type + " casec_RS",
+ cpEntryListToArray(casec_RS), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " casec_RS[" + casec_RS.size()
+ + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type + " caseet_RS",
+ cpEntryListToArray(caseet_RS), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " caseet_RS["
+ + caseet_RS.size() + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type + " caseec_RU",
+ cpEntryListToArray(caseec_RU), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " caseec_RU["
+ + caseec_RU.size() + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type + " cases_RU",
+ cpEntryListToArray(cases_RU), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " cases_RU[" + cases_RU.size()
+ + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type
+ + " casearray_N", casearray_N.toArray(), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " casearray_N["
+ + casearray_N.size() + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type
+ + " nesttype_RS", cpEntryListToArray(nesttype_RS),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " nesttype_RS["
+ + nesttype_RS.size() + "]");
+
+ encodedBand = encodeBandInt(
+ contextStr + "_" + type + " nestpair_N", nestpair_N
+ .toArray(), Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " nestpair_N["
+ + nestpair_N.size() + "]");
+
+ encodedBand = encodeBandInt(contextStr + "_" + type
+ + " nestname_RU", cpEntryListToArray(nestname_RU),
+ Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + contextStr + "_" + type + " nestname_RU["
+ + nestname_RU.size() + "]");
+ }
+ }
+
+ private int[] tagListToArray(List t2) {
+ int[] ints = new int[t2.size()];
+ for (int i = 0; i < ints.length; i++) {
+ ints[i] = ((String)t2.get(i)).charAt(0);
+ }
+ return ints;
+ }
+
+ /**
+ * Add an annotation to this set of bands
+ *
+ * @param desc
+ * @param nameRU
+ * @param t
+ * @param values
+ * @param caseArrayN
+ * @param nestTypeRS
+ * @param nestNameRU
+ * @param nestPairN
+ */
+ public void addParameterAnnotation(int numParams, int[] annoN, IntList pairN, List typeRS, List nameRU, List t, List values, List caseArrayN, List nestTypeRS, List nestNameRU, List nestPairN) {
+ param_NB.add(numParams);
+ for (int i = 0; i < annoN.length; i++) {
+ anno_N.add(annoN[i]);
+ }
+ pair_N.addAll(pairN);
+ for (Iterator iterator = typeRS.iterator(); iterator.hasNext();) {
+ String desc = (String) iterator.next();
+ type_RS.add(cpBands.getCPSignature(desc));
+ }
+ for (Iterator iterator = nameRU.iterator(); iterator.hasNext();) {
+ String name = (String) iterator.next();
+ name_RU.add(cpBands.getCPUtf8(name));
+ }
+ Iterator valuesIterator = values.iterator();
+ for (Iterator iterator = t.iterator(); iterator.hasNext();) {
+ String tag = (String) iterator.next();
+ T.add(tag);
+ if (tag.equals("B") || tag.equals("C") || tag.equals("I")
+ || tag.equals("S") || tag.equals("Z")) {
+ Integer value = (Integer)valuesIterator.next();
+ caseI_KI.add(cpBands.getConstant(value));
+ } else if (tag.equals("D")) {
+ Double value = (Double)valuesIterator.next();
+ caseD_KD.add(cpBands.getConstant(value));
+ } else if (tag.equals("F")) {
+ Float value = (Float)valuesIterator.next();
+ caseF_KF.add(cpBands.getConstant(value));
+ } else if (tag.equals("J")) {
+ Long value = (Long)valuesIterator.next();
+ caseJ_KJ.add(cpBands.getConstant(value));
+ } else if (tag.equals("c")) {
+ String value = (String)valuesIterator.next();
+ casec_RS.add(cpBands.getCPSignature(value));
+ } else if (tag.equals("e")) {
+ String value = (String)valuesIterator.next();
+ String value2 = (String)valuesIterator.next();
+ caseet_RS.add(cpBands.getCPSignature(value));
+ caseec_RU.add(cpBands.getCPUtf8(value2));
+ } else if (tag.equals("s")) {
+ String value = (String)valuesIterator.next();
+ cases_RU.add(cpBands.getCPUtf8(value));
+ }
+ // do nothing here for [ or @ (handled below)
+ }
+ for (Iterator iterator = caseArrayN.iterator(); iterator.hasNext();) {
+ int arraySize = ((Integer)iterator.next()).intValue();
+ casearray_N.add(arraySize);
+ numBackwardsCalls += arraySize;
+ }
+ for (Iterator iterator = nestTypeRS.iterator(); iterator.hasNext();) {
+ String type = (String) iterator.next();
+ nesttype_RS.add(cpBands.getCPSignature(type));
+ }
+ for (Iterator iterator = nestNameRU.iterator(); iterator.hasNext();) {
+ String name = (String) iterator.next();
+ nestname_RU.add(cpBands.getCPUtf8(name));
+ }
+ for (Iterator iterator = nestPairN.iterator(); iterator.hasNext();) {
+ Integer numPairs = (Integer) iterator.next();
+ nestpair_N.add(numPairs.intValue());
+ numBackwardsCalls += numPairs.intValue();
+ }
+ }
+
+ /**
+ * Add an annotation to this set of bands
+ *
+ * @param desc
+ * @param nameRU
+ * @param t
+ * @param values
+ * @param caseArrayN
+ * @param nestTypeRS
+ * @param nestNameRU
+ * @param nestPairN
+ */
+ public void addAnnotation(String desc, List nameRU, List t, List values, List caseArrayN, List nestTypeRS, List nestNameRU, List nestPairN) {
+ type_RS.add(cpBands.getCPSignature(desc));
+ pair_N.add(nameRU.size());
+
+ for (Iterator iterator = nameRU.iterator(); iterator.hasNext();) {
+ String name = (String) iterator.next();
+ name_RU.add(cpBands.getCPUtf8(name));
+ }
+
+ Iterator valuesIterator = values.iterator();
+ for (Iterator iterator = t.iterator(); iterator.hasNext();) {
+ String tag = (String) iterator.next();
+ T.add(tag);
+ if (tag.equals("B") || tag.equals("C") || tag.equals("I")
+ || tag.equals("S") || tag.equals("Z")) {
+ Integer value = (Integer)valuesIterator.next();
+ caseI_KI.add(cpBands.getConstant(value));
+ } else if (tag.equals("D")) {
+ Double value = (Double)valuesIterator.next();
+ caseD_KD.add(cpBands.getConstant(value));
+ } else if (tag.equals("F")) {
+ Float value = (Float)valuesIterator.next();
+ caseF_KF.add(cpBands.getConstant(value));
+ } else if (tag.equals("J")) {
+ Long value = (Long)valuesIterator.next();
+ caseJ_KJ.add(cpBands.getConstant(value));
+ } else if (tag.equals("c")) {
+ String value = (String)valuesIterator.next();
+ casec_RS.add(cpBands.getCPSignature(value));
+ } else if (tag.equals("e")) {
+ String value = (String)valuesIterator.next();
+ String value2 = (String)valuesIterator.next();
+ caseet_RS.add(cpBands.getCPSignature(value));
+ caseec_RU.add(cpBands.getCPUtf8(value2));
+ } else if (tag.equals("s")) {
+ String value = (String)valuesIterator.next();
+ cases_RU.add(cpBands.getCPUtf8(value));
+ }
+ // do nothing here for [ or @ (handled below)
+ }
+ for (Iterator iterator = caseArrayN.iterator(); iterator.hasNext();) {
+ int arraySize = ((Integer)iterator.next()).intValue();
+ casearray_N.add(arraySize);
+ numBackwardsCalls += arraySize;
+ }
+ for (Iterator iterator = nestTypeRS.iterator(); iterator.hasNext();) {
+ String type = (String) iterator.next();
+ nesttype_RS.add(cpBands.getCPSignature(type));
+ }
+ for (Iterator iterator = nestNameRU.iterator(); iterator.hasNext();) {
+ String name = (String) iterator.next();
+ nestname_RU.add(cpBands.getCPUtf8(name));
+ }
+ for (Iterator iterator = nestPairN.iterator(); iterator.hasNext();) {
+ Integer numPairs = (Integer) iterator.next();
+ nestpair_N.add(numPairs.intValue());
+ numBackwardsCalls += numPairs.intValue();
+ }
+ }
+
+ /**
+ * Returns true if any annotations have been added to this set of bands
+ */
+ public boolean hasContent() {
+ return type_RS.size() > 0;
+ }
+
+ public int numBackwardsCalls() {
+ return numBackwardsCalls;
+ }
+
+ public void incrementAnnoN() {
+ anno_N.increment(anno_N.size() - 1);
+ }
+
+ public void newEntryInAnnoN() {
+ anno_N.add(1);
+ }
+
+ /**
+ * Remove the latest annotation that was added to this group
+ */
+ public void removeLatest() {
+ int latest = anno_N.remove(anno_N.size() -1);
+ for (int i = 0; i < latest; i++) {
+ type_RS.remove(type_RS.size() - 1);
+ int pairs = pair_N.remove(pair_N.size() - 1);
+ for (int j = 0; j < pairs; j++) {
+ removeOnePair();
+ }
+ }
+ }
+
+ /*
+ * Convenience method for removeLatest
+ */
+ private void removeOnePair() {
+ String tag = (String) T.remove(T.size() - 1);
+ if (tag.equals("B") || tag.equals("C") || tag.equals("I")
+ || tag.equals("S") || tag.equals("Z")) {
+ caseI_KI.remove(caseI_KI.size() - 1);
+ } else if (tag.equals("D")) {
+ caseD_KD.remove(caseD_KD.size() - 1);
+ } else if (tag.equals("F")) {
+ caseF_KF.remove(caseF_KF.size() - 1);
+ } else if (tag.equals("J")) {
+ caseJ_KJ.remove(caseJ_KJ.size() - 1);
+ } else if (tag.equals("C")) {
+ casec_RS.remove(casec_RS.size() - 1);
+ } else if (tag.equals("e")) {
+ caseet_RS.remove(caseet_RS.size() - 1);
+ caseec_RU.remove(caseet_RS.size() - 1);
+ } else if (tag.equals("s")) {
+ cases_RU.remove(cases_RU.size() - 1);
+ } else if (tag.equals("[")) {
+ int arraySize = casearray_N.remove(casearray_N.size() - 1);
+ numBackwardsCalls -= arraySize;
+ for (int k = 0; k < arraySize; k++) {
+ removeOnePair();
+ }
+ } else if (tag.equals("@")) {
+ nesttype_RS.remove(nesttype_RS.size() - 1);
+ int numPairs = nestpair_N.remove(nestpair_N.size() - 1);
+ numBackwardsCalls -= numPairs;
+ for (int i = 0; i < numPairs; i++) {
+ removeOnePair();
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/NewAttribute.java b/src/main/java/org/apache/harmony/pack200/NewAttribute.java
new file mode 100644
index 0000000..54b867b
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/NewAttribute.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Label;
+
+/**
+ * NewAttribute extends <code>Attribute</code> and manages unknown attributes
+ * encountered by ASM that have had a layout definition given to pack200 (e.g.
+ * via one of the -C, -M, -F or -D command line options)
+ */
+public class NewAttribute extends Attribute {
+
+ private boolean contextClass = false;
+ private boolean contextMethod = false;
+ private boolean contextField = false;
+ private boolean contextCode = false;
+
+ private final String layout;
+ private byte[] contents;
+ private int codeOff;
+ private Label[] labels;
+ private ClassReader classReader;
+ private char[] buf;
+
+ public NewAttribute(String type, String layout, int context) {
+ super(type);
+ this.layout = layout;
+ addContext(context);
+ }
+
+ public NewAttribute(ClassReader classReader, String type, String layout, byte[] contents, char[] buf,
+ int codeOff, Label[] labels) {
+ super(type);
+ this.classReader = classReader;
+ this.contents = contents;
+ this.layout = layout;
+ this.codeOff = codeOff;
+ this.labels = labels;
+ this.buf = buf;
+ }
+
+ public void addContext(int context) {
+ switch(context) {
+ case AttributeDefinitionBands.CONTEXT_CLASS:
+ contextClass = true;
+ break;
+ case AttributeDefinitionBands.CONTEXT_METHOD:
+ contextMethod = true;
+ break;
+ case AttributeDefinitionBands.CONTEXT_FIELD:
+ contextField = true;
+ break;
+ case AttributeDefinitionBands.CONTEXT_CODE:
+ contextCode = true;
+ break;
+ }
+ }
+
+ public boolean isContextClass() {
+ return contextClass;
+ }
+
+ public boolean isContextMethod() {
+ return contextMethod;
+ }
+
+ public boolean isContextField() {
+ return contextField;
+ }
+
+ public boolean isContextCode() {
+ return contextCode;
+ }
+
+ public String getLayout() {
+ return layout;
+ }
+
+ public boolean isUnknown() {
+ return false;
+ }
+
+ public boolean isCodeAttribute() {
+ return codeOff != -1;
+ }
+
+ protected Attribute read(ClassReader cr, int off, int len, char[] buf,
+ int codeOff, Label[] labels) {
+ byte[] attributeContents = new byte[len];
+ System.arraycopy(cr.b, off, attributeContents, 0, len);
+ return new NewAttribute(cr, type, layout, attributeContents, buf, codeOff,
+ labels);
+ }
+
+ public boolean isUnknown(int context) {
+ switch(context) {
+ case AttributeDefinitionBands.CONTEXT_CLASS:
+ return !contextClass;
+ case AttributeDefinitionBands.CONTEXT_METHOD:
+ return !contextMethod;
+ case AttributeDefinitionBands.CONTEXT_FIELD:
+ return !contextField;
+ case AttributeDefinitionBands.CONTEXT_CODE:
+ return !contextCode;
+ }
+ return false;
+ }
+
+ public String readUTF8(int index) {
+ return classReader.readUTF8(index, buf);
+ }
+
+ public String readClass(int index) {
+ return classReader.readClass(index, buf);
+ }
+
+ public Object readConst(int index) {
+ return classReader.readConst(index, buf);
+ }
+
+ public byte[] getBytes() {
+ return contents;
+ }
+
+ public Label getLabel(int index) {
+ return labels[index];
+ }
+
+ /**
+ * ErrorAttribute extends <code>NewAttribute</code> and manages attributes
+ * encountered by ASM that have had an error action specified to pack200
+ * (e.g. via one of the -C, -M, -F or -D command line options such as
+ * -Cattribute-name=error)
+ */
+ public static class ErrorAttribute extends NewAttribute {
+
+ public ErrorAttribute(String type, int context) {
+ super(type, "", context);
+ }
+
+ protected Attribute read(ClassReader cr, int off, int len, char[] buf,
+ int codeOff, Label[] labels) {
+ throw new Error("Attribute " + type + " was found");
+ }
+
+ }
+
+ /**
+ * StripAttribute extends <code>NewAttribute</code> and manages attributes
+ * encountered by ASM that have had an strip action specified to pack200
+ * (e.g. via one of the -C, -M, -F or -D command line options such as
+ * -Cattribute-name=strip)
+ */
+ public static class StripAttribute extends NewAttribute {
+
+ public StripAttribute(String type, int context) {
+ super(type, "", context);
+ }
+
+ protected Attribute read(ClassReader cr, int off, int len, char[] buf,
+ int codeOff, Label[] labels) {
+ // TODO Not sure if this works, can we really strip an attribute if we don't know the layout?
+ return null;
+ }
+ }
+
+ /**
+ * PassAttribute extends <code>NewAttribute</code> and manages attributes
+ * encountered by ASM that have had an pass action specified to pack200
+ * (e.g. via one of the -C, -M, -F or -D command line options such as
+ * -Cattribute-name=pass)
+ */
+ public static class PassAttribute extends NewAttribute {
+
+ public PassAttribute(String type, int context) {
+ super(type, "", context);
+ }
+
+ protected Attribute read(ClassReader cr, int off, int len, char[] buf,
+ int codeOff, Label[] labels) {
+ throw new Segment.PassException();
+ }
+
+ }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/NewAttributeBands.java b/src/main/java/org/apache/harmony/pack200/NewAttributeBands.java
new file mode 100644
index 0000000..fcbd1b7
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/NewAttributeBands.java
@@ -0,0 +1,965 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.harmony.pack200.AttributeDefinitionBands.AttributeDefinition;
+import org.objectweb.asm.Label;
+
+/**
+ * Set of bands relating to a non-predefined attribute that has had a layout
+ * definition given to pack200 (e.g. via one of the -C, -M, -F or -D command
+ * line options)
+ */
+public class NewAttributeBands extends BandSet {
+
+ protected List attributeLayoutElements;
+ private int[] backwardsCallCounts;
+ private final CpBands cpBands;
+ private final AttributeDefinition def;
+ private boolean usedAtLeastOnce;
+
+ // used when parsing
+ private Integral lastPIntegral;
+
+ public NewAttributeBands(int effort, CpBands cpBands, SegmentHeader header, AttributeDefinition def) throws IOException {
+ super(effort, header);
+ this.def = def;
+ this.cpBands = cpBands;
+ parseLayout();
+ }
+
+ public void addAttribute(NewAttribute attribute) {
+ usedAtLeastOnce = true;
+ InputStream stream = new ByteArrayInputStream(attribute.getBytes());
+ for (Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) {
+ AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+ layoutElement.addAttributeToBand(attribute, stream);
+ }
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ for (Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) {
+ AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+ layoutElement.pack(out);
+ }
+ }
+
+ public String getAttributeName() {
+ return def.name.getUnderlyingString();
+ }
+
+ public int getFlagIndex() {
+ return def.index;
+ }
+
+ public int[] numBackwardsCalls() {
+ return backwardsCallCounts;
+ }
+
+ public boolean isUsedAtLeastOnce() {
+ return usedAtLeastOnce;
+ }
+
+ private void parseLayout() throws IOException {
+ String layout = def.layout.getUnderlyingString();
+ if (attributeLayoutElements == null) {
+ attributeLayoutElements = new ArrayList();
+ StringReader stream = new StringReader(layout);
+ AttributeLayoutElement e;
+ while ((e = readNextAttributeElement(stream)) != null) {
+ attributeLayoutElements.add(e);
+ }
+ resolveCalls();
+ }
+ }
+
+ /**
+ * Resolve calls in the attribute layout and returns the number of backwards
+ * callables
+ *
+ * @param tokens -
+ * the attribute layout as a List of AttributeElements
+ */
+ private void resolveCalls() {
+ for (int i = 0; i < attributeLayoutElements.size(); i++) {
+ AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements
+ .get(i);
+ if (element instanceof Callable) {
+ Callable callable = (Callable) element;
+ List body = callable.body; // Look for calls in the body
+ for (int iIndex = 0; iIndex < body.size(); iIndex++) {
+ LayoutElement layoutElement = (LayoutElement) body
+ .get(iIndex);
+ // Set the callable for each call
+ resolveCallsForElement(i, callable, layoutElement);
+ }
+ }
+ }
+ int backwardsCallableIndex = 0;
+ for (int i = 0; i < attributeLayoutElements.size(); i++) {
+ AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements
+ .get(i);
+ if (element instanceof Callable) {
+ Callable callable = (Callable) element;
+ if(callable.isBackwardsCallable) {
+ callable.setBackwardsCallableIndex(backwardsCallableIndex);
+ backwardsCallableIndex++;
+ }
+ }
+ }
+ backwardsCallCounts = new int[backwardsCallableIndex];
+ }
+
+ private void resolveCallsForElement(int i,
+ Callable currentCallable, LayoutElement layoutElement) {
+ if (layoutElement instanceof Call) {
+ Call call = (Call) layoutElement;
+ int index = call.callableIndex;
+ if (index == 0) { // Calls the parent callable
+ call.setCallable(currentCallable);
+ } else if (index > 0) { // Forwards call
+ for (int k = i + 1; k < attributeLayoutElements.size(); k++) {
+ AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements
+ .get(k);
+ if (el instanceof Callable) {
+ index--;
+ if (index == 0) {
+ call.setCallable((Callable) el);
+ break;
+ }
+ }
+ }
+ } else { // Backwards call
+ for (int k = i - 1; k >= 0; k--) {
+ AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements
+ .get(k);
+ if (el instanceof Callable) {
+ index++;
+ if (index == 0) {
+ call.setCallable((Callable) el);
+ break;
+ }
+ }
+ }
+ }
+ } else if (layoutElement instanceof Replication) {
+ List children = ((Replication)layoutElement).layoutElements;
+ for (Iterator iterator = children.iterator(); iterator.hasNext();) {
+ LayoutElement object = (LayoutElement) iterator.next();
+ resolveCallsForElement(i, currentCallable, object);
+ }
+ }
+ }
+
+ private AttributeLayoutElement readNextAttributeElement(StringReader stream)
+ throws IOException {
+ stream.mark(1);
+ int nextChar = stream.read();
+ if (nextChar == -1) {
+ return null;
+ }
+ if (nextChar == '[') {
+ List body = readBody(getStreamUpToMatchingBracket(stream));
+ return new Callable(body);
+ } else {
+ stream.reset();
+ return readNextLayoutElement(stream);
+ }
+ }
+
+ private LayoutElement readNextLayoutElement(StringReader stream)
+ throws IOException {
+ int nextChar = stream.read();
+ if (nextChar == -1) {
+ return null;
+ }
+
+ switch (nextChar) {
+ // Integrals
+ case 'B':
+ case 'H':
+ case 'I':
+ case 'V':
+ return new Integral(new String(new char[] { (char)nextChar }));
+ case 'S':
+ case 'F':
+ return new Integral(new String(new char[] { (char)nextChar,
+ (char) stream.read() }));
+ case 'P':
+ stream.mark(1);
+ if (stream.read() != 'O') {
+ stream.reset();
+ lastPIntegral = new Integral("P" + (char) stream.read());
+ return lastPIntegral;
+ } else {
+ lastPIntegral = new Integral("PO" + (char) stream.read(), lastPIntegral);
+ return lastPIntegral;
+ }
+ case 'O':
+ stream.mark(1);
+ if (stream.read() != 'S') {
+ stream.reset();
+ return new Integral("O" + (char) stream.read(), lastPIntegral);
+ } else {
+ return new Integral("OS" + (char) stream.read(), lastPIntegral);
+ }
+
+ // Replication
+ case 'N':
+ char uint_type = (char) stream.read();
+ stream.read(); // '['
+ String str = readUpToMatchingBracket(stream);
+ return new Replication("" + uint_type, str);
+
+ // Union
+ case 'T':
+ String int_type = "" + (char) stream.read();
+ if (int_type.equals("S")) {
+ int_type += (char) stream.read();
+ }
+ List unionCases = new ArrayList();
+ UnionCase c;
+ while ((c = readNextUnionCase(stream)) != null) {
+ unionCases.add(c);
+ }
+ stream.read(); // '('
+ stream.read(); // ')'
+ stream.read(); // '['
+ List body = null;
+ stream.mark(1);
+ char next = (char) stream.read();
+ if (next != ']') {
+ stream.reset();
+ body = readBody(getStreamUpToMatchingBracket(stream));
+ }
+ return new Union(int_type, unionCases, body);
+
+ // Call
+ case '(':
+ int number = readNumber(stream).intValue();
+ stream.read(); // ')'
+ return new Call(number);
+ // Reference
+ case 'K':
+ case 'R':
+ String string = "" + (char)nextChar + (char) stream.read();
+ char nxt = (char) stream.read();
+ string += nxt;
+ if (nxt == 'N') {
+ string += (char) stream.read();
+ }
+ return new Reference(string);
+ }
+ return null;
+ }
+
+ /**
+ * Read a UnionCase from the stream
+ *
+ * @param stream
+ * @return
+ * @throws IOException
+ */
+ private UnionCase readNextUnionCase(StringReader stream) throws IOException {
+ stream.mark(2);
+ stream.read(); // '('
+ char next = (char) stream.read();
+ if (next == ')') {
+ stream.reset();
+ return null;
+ } else {
+ stream.reset();
+ stream.read(); // '('
+ }
+ List tags = new ArrayList();
+ Integer nextTag;
+ do {
+ nextTag = readNumber(stream);
+ if(nextTag != null) {
+ tags.add(nextTag);
+ stream.read(); // ',' or ')'
+ }
+ } while (nextTag != null);
+ stream.read(); // '['
+ stream.mark(1);
+ next = (char) stream.read();
+ if (next == ']') {
+ return new UnionCase(tags);
+ } else {
+ stream.reset();
+ return new UnionCase(tags,
+ readBody(getStreamUpToMatchingBracket(stream)));
+ }
+ }
+
+ /**
+ * An AttributeLayoutElement is a part of an attribute layout and has one or
+ * more bands associated with it, which transmit the AttributeElement data
+ * for successive Attributes of this type.
+ */
+ public interface AttributeLayoutElement {
+
+ public void addAttributeToBand(NewAttribute attribute, InputStream stream);
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception;
+
+ public void renumberBci(IntList bciRenumbering, Map labelsToOffsets);
+
+ }
+
+ public abstract class LayoutElement implements AttributeLayoutElement {
+
+ protected int getLength(char uint_type) {
+ int length = 0;
+ switch (uint_type) {
+ case 'B':
+ length = 1;
+ break;
+ case 'H':
+ length = 2;
+ break;
+ case 'I':
+ length = 4;
+ break;
+ case 'V':
+ length = 0;
+ break;
+ }
+ return length;
+ }
+ }
+
+ public class Integral extends LayoutElement {
+
+ private final String tag;
+
+ private final List band = new ArrayList();
+ private final BHSDCodec defaultCodec;
+
+ // used for bytecode offsets (OH and POH)
+ private Integral previousIntegral;
+ private int previousPValue;
+
+ public Integral(String tag) {
+ this.tag = tag;
+ this.defaultCodec = getCodec(tag);
+ }
+
+ public Integral(String tag, Integral previousIntegral) {
+ this.tag = tag;
+ this.defaultCodec = getCodec(tag);
+ this.previousIntegral = previousIntegral;
+ }
+
+ public String getTag() {
+ return tag;
+ }
+
+ public void addAttributeToBand(NewAttribute attribute,
+ InputStream stream) {
+ Object val = null;
+ int value = 0;
+ if (tag.equals("B") || tag.equals("FB")) {
+ value = readInteger(1, stream) & 0xFF; // unsigned byte
+ } else if (tag.equals("SB")) {
+ value = readInteger(1, stream);
+ } else if (tag.equals("H") || tag.equals("FH")) {
+ value = readInteger(2, stream) & 0xFFFF; // unsigned short
+ } else if (tag.equals("SH")) {
+ value = readInteger(2, stream);
+ } else if (tag.equals("I") || tag.equals("FI")) {
+ value = readInteger(4, stream);
+ } else if (tag.equals("SI")) {
+ value = readInteger(4, stream);
+ } else if (tag.equals("V") || tag.equals("FV") || tag.equals("SV")) {
+ // Not currently supported
+ } else if (tag.startsWith("PO") || tag.startsWith("OS")) {
+ char uint_type = tag.substring(2).toCharArray()[0];
+ int length = getLength(uint_type);
+ value = readInteger(length, stream);
+ value += previousIntegral.previousPValue;
+ val = attribute.getLabel(value);
+ previousPValue = value;
+ } else if (tag.startsWith("P")) {
+ char uint_type = tag.substring(1).toCharArray()[0];
+ int length = getLength(uint_type);
+ value = readInteger(length, stream);
+ val = attribute.getLabel(value);
+ previousPValue = value;
+ } else if (tag.startsWith("O")) {
+ char uint_type = tag.substring(1).toCharArray()[0];
+ int length = getLength(uint_type);
+ value = readInteger(length, stream);
+ value += previousIntegral.previousPValue;
+ val = attribute.getLabel(value);
+ previousPValue = value;
+ }
+ if(val == null) {
+ val = new Integer(value);
+ }
+ band.add(val);
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ PackingUtils.log("Writing new attribute bands...");
+ byte[] encodedBand = encodeBandInt(tag, integerListToArray(band),
+ defaultCodec);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + tag + "[" + band.size() + "]");
+ }
+
+ public int latestValue() {
+ return ((Integer)band.get(band.size() - 1)).intValue();
+ }
+
+ public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+ if(tag.startsWith("O") || tag.startsWith("PO")) {
+ renumberOffsetBci(previousIntegral.band, bciRenumbering, labelsToOffsets);
+ } else if (tag.startsWith("P")) {
+ for (int i = band.size() - 1; i >= 0; i--) {
+ Object label = band.get(i);
+ if (label instanceof Integer) {
+ break;
+ } else if (label instanceof Label) {
+ band.remove(i);
+ Integer bytecodeIndex = (Integer) labelsToOffsets
+ .get(label);
+ band.add(i, new Integer(bciRenumbering.get(bytecodeIndex
+ .intValue())));
+ }
+ }
+ }
+ }
+
+ private void renumberOffsetBci(List relative,
+ IntList bciRenumbering, Map labelsToOffsets) {
+ for (int i = band.size() - 1; i >= 0; i--) {
+ Object label = band.get(i);
+ if (label instanceof Integer) {
+ break;
+ } else if (label instanceof Label) {
+ band.remove(i);
+ Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
+ Integer renumberedOffset = new Integer(bciRenumbering
+ .get(bytecodeIndex.intValue())
+ - ((Integer) relative.get(i)).intValue());
+ band.add(i, renumberedOffset);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * A replication is an array of layout elements, with an associated count
+ */
+ public class Replication extends LayoutElement {
+
+ private final Integral countElement;
+
+ private final List layoutElements = new ArrayList();
+
+ public Integral getCountElement() {
+ return countElement;
+ }
+
+ public List getLayoutElements() {
+ return layoutElements;
+ }
+
+ public Replication(String tag, String contents) throws IOException {
+ this.countElement = new Integral(tag);
+ StringReader stream = new StringReader(contents);
+ LayoutElement e;
+ while ((e = readNextLayoutElement(stream)) != null) {
+ layoutElements.add(e);
+ }
+ }
+
+ public void addAttributeToBand(NewAttribute attribute,
+ InputStream stream) {
+ countElement.addAttributeToBand(attribute, stream);
+ int count = countElement.latestValue();
+ for (int i = 0; i < count; i++) {
+ for (Iterator iterator = layoutElements.iterator(); iterator.hasNext();) {
+ AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+ layoutElement.addAttributeToBand(attribute, stream);
+ }
+ }
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ countElement.pack(out);
+ for (Iterator iterator = layoutElements.iterator(); iterator.hasNext();) {
+ AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+ layoutElement.pack(out);
+ }
+ }
+
+ public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+ for (Iterator iterator = layoutElements.iterator(); iterator.hasNext();) {
+ AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+ layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
+ }
+ }
+ }
+
+ /**
+ * A Union is a type of layout element where the tag value acts as a
+ * selector for one of the union cases
+ */
+ public class Union extends LayoutElement {
+
+ private final Integral unionTag;
+ private final List unionCases;
+ private final List defaultCaseBody;
+
+ public Union(String tag, List unionCases, List body) {
+ this.unionTag = new Integral(tag);
+ this.unionCases = unionCases;
+ this.defaultCaseBody = body;
+ }
+
+ public void addAttributeToBand(NewAttribute attribute,
+ InputStream stream) {
+ unionTag.addAttributeToBand(attribute, stream);
+ long tag = unionTag.latestValue();
+ boolean defaultCase = true;
+ for (int i = 0; i < unionCases.size(); i++) {
+ UnionCase element = (UnionCase) unionCases.get(i);
+ if (element.hasTag(tag)) {
+ defaultCase = false;
+ element.addAttributeToBand(attribute, stream);
+ }
+ }
+ if (defaultCase) {
+ for (int i = 0; i < defaultCaseBody.size(); i++) {
+ LayoutElement element = (LayoutElement) defaultCaseBody
+ .get(i);
+ element.addAttributeToBand(attribute, stream);
+ }
+ }
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ unionTag.pack(out);
+ for (Iterator iterator = unionCases.iterator(); iterator.hasNext();) {
+ UnionCase unionCase = (UnionCase) iterator.next();
+ unionCase.pack(out);
+ }
+ for (Iterator iterator = defaultCaseBody.iterator(); iterator
+ .hasNext();) {
+ AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator
+ .next();
+ layoutElement.pack(out);
+ }
+ }
+
+ public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+ for (Iterator iterator = unionCases.iterator(); iterator.hasNext();) {
+ UnionCase unionCase = (UnionCase) iterator.next();
+ unionCase.renumberBci(bciRenumbering, labelsToOffsets);
+ }
+ for (Iterator iterator = defaultCaseBody.iterator(); iterator
+ .hasNext();) {
+ AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator
+ .next();
+ layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
+ }
+ }
+
+ public Integral getUnionTag() {
+ return unionTag;
+ }
+
+ public List getUnionCases() {
+ return unionCases;
+ }
+
+ public List getDefaultCaseBody() {
+ return defaultCaseBody;
+ }
+ }
+
+ public class Call extends LayoutElement {
+
+ private final int callableIndex;
+ private Callable callable;
+
+ public Call(int callableIndex) {
+ this.callableIndex = callableIndex;
+ }
+
+ public void setCallable(Callable callable) {
+ this.callable = callable;
+ if (callableIndex < 1) {
+ callable.setBackwardsCallable();
+ }
+ }
+
+ public void addAttributeToBand(NewAttribute attribute,
+ InputStream stream) {
+ callable.addAttributeToBand(attribute, stream);
+ if(callableIndex < 1) {
+ callable.addBackwardsCall();
+ }
+ }
+
+ public void pack(OutputStream out) {
+ // do nothing here as pack will be called on the callable at another time
+ }
+
+ public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+ // do nothing here as renumberBci will be called on the callable at another time
+ }
+
+ public int getCallableIndex() {
+ return callableIndex;
+ }
+
+ public Callable getCallable() {
+ return callable;
+ }
+ }
+
+ /**
+ * Constant Pool Reference
+ */
+ public class Reference extends LayoutElement {
+
+ private final String tag;
+
+ private List band;
+
+ private boolean nullsAllowed = false;
+
+ public Reference(String tag) {
+ this.tag = tag;
+ nullsAllowed = tag.indexOf('N') != -1;
+ }
+
+ public void addAttributeToBand(NewAttribute attribute,
+ InputStream stream) {
+ int index = readInteger(4, stream);
+ if(tag.startsWith("RC")) { // Class
+ band.add(cpBands.getCPClass(attribute.readClass(index)));
+ } else if (tag.startsWith("RU")) { // UTF8 String
+ band.add(cpBands.getCPUtf8(attribute.readUTF8(index)));
+ } else if (tag.startsWith("RS")) { // Signature
+ band.add(cpBands.getCPSignature(attribute.readUTF8(index)));
+ } else { // Constant
+ band.add(cpBands.getConstant(attribute.readConst(index)));
+ }
+ // TODO method and field references
+ }
+
+ public String getTag() {
+ return tag;
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ int[] ints;
+ if(nullsAllowed) {
+ ints = cpEntryOrNullListToArray(band);
+ } else {
+ ints = cpEntryListToArray(band);
+ }
+ byte[] encodedBand = encodeBandInt(tag, ints, Codec.UNSIGNED5);
+ out.write(encodedBand);
+ PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+ + tag + "[" + ints.length + "]");
+ }
+
+ public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+ // nothing to do here
+ }
+
+ }
+
+ public class Callable implements AttributeLayoutElement {
+
+ private final List body;
+
+ private boolean isBackwardsCallable;
+
+ private int backwardsCallableIndex;
+
+ public Callable(List body) throws IOException {
+ this.body = body;
+ }
+
+ public void setBackwardsCallableIndex(int backwardsCallableIndex) {
+ this.backwardsCallableIndex = backwardsCallableIndex;
+ }
+
+ public void addBackwardsCall() {
+ backwardsCallCounts[backwardsCallableIndex]++;
+ }
+
+ public boolean isBackwardsCallable() {
+ return isBackwardsCallable;
+ }
+
+ /**
+ * Tells this Callable that it is a backwards callable
+ */
+ public void setBackwardsCallable() {
+ this.isBackwardsCallable = true;
+ }
+
+ public void addAttributeToBand(NewAttribute attribute,
+ InputStream stream) {
+ for (Iterator iterator = body.iterator(); iterator.hasNext();) {
+ AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+ layoutElement.addAttributeToBand(attribute, stream);
+ }
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ for (Iterator iterator = body.iterator(); iterator.hasNext();) {
+ AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+ layoutElement.pack(out);
+ }
+ }
+
+ public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+ for (Iterator iterator = body.iterator(); iterator.hasNext();) {
+ AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+ layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
+ }
+ }
+
+ public List getBody() {
+ return body;
+ }
+ }
+
+ /**
+ * A Union case
+ */
+ public class UnionCase extends LayoutElement {
+
+ private final List body;
+
+ private final List tags;
+
+ public UnionCase(List tags) {
+ this.tags = tags;
+ this.body = Collections.EMPTY_LIST;
+ }
+
+ public boolean hasTag(long l) {
+ return tags.contains(new Integer((int) l));
+ }
+
+ public UnionCase(List tags, List body) throws IOException {
+ this.tags = tags;
+ this.body = body;
+ }
+
+ public void addAttributeToBand(NewAttribute attribute,
+ InputStream stream) {
+ for (int i = 0; i < body.size(); i++) {
+ LayoutElement element = (LayoutElement) body.get(i);
+ element.addAttributeToBand(attribute, stream);
+ }
+ }
+
+ public void pack(OutputStream out) throws IOException, Pack200Exception {
+ for (int i = 0; i < body.size(); i++) {
+ LayoutElement element = (LayoutElement) body.get(i);
+ element.pack(out);
+ }
+ }
+
+ public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+ for (int i = 0; i < body.size(); i++) {
+ LayoutElement element = (LayoutElement) body.get(i);
+ element.renumberBci(bciRenumbering, labelsToOffsets);
+ }
+ }
+
+ public List getBody() {
+ return body;
+ }
+ }
+
+ /**
+ * Utility method to get the contents of the given stream, up to the next
+ * ']', (ignoring pairs of brackets '[' and ']')
+ *
+ * @param stream
+ * @return
+ * @throws IOException
+ */
+ private StringReader getStreamUpToMatchingBracket(StringReader stream)
+ throws IOException {
+ StringBuffer sb = new StringBuffer();
+ int foundBracket = -1;
+ while (foundBracket != 0) {
+ char c = (char) stream.read();
+ if (c == ']') {
+ foundBracket++;
+ }
+ if (c == '[') {
+ foundBracket--;
+ }
+ if (!(foundBracket == 0)) {
+ sb.append(c);
+ }
+ }
+ return new StringReader(sb.toString());
+ }
+
+ private int readInteger(int i, InputStream stream) {
+ int result = 0;
+ for (int j = 0; j < i; j++) {
+ try {
+ result = result << 8 | stream.read();
+ } catch (IOException e) {
+ throw new RuntimeException("Error reading unknown attribute");
+ }
+ }
+ // use casting to preserve sign
+ if(i == 1) result = (byte) result;
+ if(i == 2) result = (short) result;
+ return result;
+ }
+
+ /**
+ * Returns the {@link BHSDCodec} that should be used for the given layout
+ * element
+ *
+ * @param layoutElement
+ */
+ private BHSDCodec getCodec(String layoutElement) {
+ if (layoutElement.indexOf('O') >= 0) {
+ return Codec.BRANCH5;
+ } else if (layoutElement.indexOf('P') >= 0) {
+ return Codec.BCI5;
+ } else if (layoutElement.indexOf('S') >= 0 && layoutElement.indexOf("KS") < 0 //$NON-NLS-1$
+ && layoutElement.indexOf("RS") < 0) { //$NON-NLS-1$
+ return Codec.SIGNED5;
+ } else if (layoutElement.indexOf('B') >= 0) {
+ return Codec.BYTE1;
+ } else {
+ return Codec.UNSIGNED5;
+ }
+ }
+
+ /**
+ * Utility method to get the contents of the given stream, up to the next
+ * ']', (ignoring pairs of brackets '[' and ']')
+ *
+ * @param stream
+ * @return
+ * @throws IOException
+ */
+ private String readUpToMatchingBracket(StringReader stream)
+ throws IOException {
+ StringBuffer sb = new StringBuffer();
+ int foundBracket = -1;
+ while (foundBracket != 0) {
+ char c = (char) stream.read();
+ if (c == ']') {
+ foundBracket++;
+ }
+ if (c == '[') {
+ foundBracket--;
+ }
+ if (!(foundBracket == 0)) {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Read a number from the stream and return it
+ *
+ * @param stream
+ * @return
+ * @throws IOException
+ */
+ private Integer readNumber(StringReader stream) throws IOException {
+ stream.mark(1);
+ char first = (char) stream.read();
+ boolean negative = first == '-';
+ if (!negative) {
+ stream.reset();
+ }
+ stream.mark(100);
+ int i;
+ int length = 0;
+ while ((i = (stream.read())) != -1 && Character.isDigit((char) i)) {
+ length++;
+ }
+ stream.reset();
+ if(length == 0) {
+ return null;
+ }
+ char[] digits = new char[length];
+ int read = stream.read(digits);
+ if (read != digits.length) {
+ throw new IOException("Error reading from the input stream");
+ }
+ return new Integer(Integer.parseInt((negative ? "-" : "") + new String(digits)));
+ }
+
+ /**
+ * Read a 'body' section of the layout from the given stream
+ *
+ * @param stream
+ * @return List of LayoutElements
+ * @throws IOException
+ */
+ private List readBody(StringReader stream) throws IOException {
+ List layoutElements = new ArrayList();
+ LayoutElement e;
+ while ((e = readNextLayoutElement(stream)) != null) {
+ layoutElements.add(e);
+ }
+ return layoutElements;
+ }
+
+ /**
+ * Renumber any bytecode indexes or offsets as described in section 5.5.2 of
+ * the pack200 specification
+ *
+ * @param bciRenumbering
+ * @param labelsToOffsets
+ */
+ public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+ for (Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) {
+ AttributeLayoutElement element = (AttributeLayoutElement) iterator.next();
+ element.renumberBci(bciRenumbering, labelsToOffsets);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/Pack200Adapter.java b/src/main/java/org/apache/harmony/pack200/Pack200Adapter.java
new file mode 100644
index 0000000..e2b284c
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/Pack200Adapter.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Provides generic JavaBeans support for the Pack/UnpackAdapters
+ */
+public abstract class Pack200Adapter {
+
+ protected static final int DEFAULT_BUFFER_SIZE = 8192;
+
+ private final PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+ private final SortedMap<String, String> properties = new TreeMap<String, String>();
+
+ public SortedMap<String, String> properties() {
+ return properties;
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+ support.addPropertyChangeListener(listener);
+ }
+
+ protected void firePropertyChange(String propertyName, Object oldValue,
+ Object newValue) {
+ support.firePropertyChange(propertyName, oldValue, newValue);
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ support.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Completion between 0..1
+ * @param value
+ */
+ protected void completed(double value) {
+ firePropertyChange("pack.progress", null, String.valueOf((int)(100*value))); //$NON-NLS-1$
+ }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/Pack200ClassReader.java b/src/main/java/org/apache/harmony/pack200/Pack200ClassReader.java
new file mode 100644
index 0000000..29c024f
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/Pack200ClassReader.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import org.objectweb.asm.ClassReader;
+
+/**
+ * Wrapper for ClassReader that enables pack200 to obtain extra class file
+ * information
+ */
+public class Pack200ClassReader extends ClassReader {
+
+ private boolean lastConstantHadWideIndex;
+ private int lastUnsignedShort;
+ private boolean anySyntheticAttributes;
+ private String fileName;
+
+ /**
+ * @param b
+ * the contents of class file in the format of bytes
+ */
+ public Pack200ClassReader(byte[] b) {
+ super(b);
+ }
+
+ public int readUnsignedShort(int index) {
+ // Doing this to check whether last load-constant instruction was ldc (18) or ldc_w (19)
+ // TODO: Assess whether this impacts on performance
+ int unsignedShort = super.readUnsignedShort(index);
+ if(b[index - 1] == 19) {
+ lastUnsignedShort = unsignedShort;
+ } else {
+ lastUnsignedShort = Short.MIN_VALUE;
+ }
+ return unsignedShort;
+ }
+
+ public Object readConst(int item, char[] buf) {
+ lastConstantHadWideIndex = item == lastUnsignedShort;
+ return super.readConst(item, buf);
+ }
+
+ public String readUTF8(int arg0, char[] arg1) {
+ String utf8 = super.readUTF8(arg0, arg1);
+ if(!anySyntheticAttributes && "Synthetic".equals(utf8)) {
+ anySyntheticAttributes = true;
+ }
+ return utf8;
+ }
+
+ public boolean lastConstantHadWideIndex() {
+ return lastConstantHadWideIndex;
+ }
+
+ public boolean hasSyntheticAttributes() {
+ return anySyntheticAttributes;
+ }
+
+ public void setFileName(String name) {
+ this.fileName = name;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/Pack200Exception.java b/src/main/java/org/apache/harmony/pack200/Pack200Exception.java
new file mode 100644
index 0000000..cb4d5e2
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/Pack200Exception.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+/**
+ * Represents a problem with a Pack200 coding/decoding issue.
+ */
+public class Pack200Exception extends Exception {
+
+ private static final long serialVersionUID = 5168177401552611803L;
+
+ /**
+ * Create a new Pack200 exception with the given message
+ *
+ * @param message
+ * the text message to display
+ */
+ public Pack200Exception(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/Pack200PackerAdapter.java b/src/main/java/org/apache/harmony/pack200/Pack200PackerAdapter.java
new file mode 100644
index 0000000..bc63a3d
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/Pack200PackerAdapter.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import org.apache.commons.compress.java.util.jar.Pack200.Packer;
+
+
+/**
+ * This class provides the binding between the standard Pack200 interface and the
+ * internal interface for (un)packing. As this uses generics for the SortedMap,
+ * this class must be compiled and run on a Java 1.5 system. However, Java 1.5
+ * is not necessary to use the internal libraries for unpacking.
+ */
+public class Pack200PackerAdapter extends Pack200Adapter implements Packer {
+
+ private final PackingOptions options = new PackingOptions();
+
+ public void pack(JarFile file, OutputStream out) throws IOException {
+ if (file == null || out == null)
+ throw new IllegalArgumentException(
+ "Must specify both input and output streams");
+ completed(0);
+ try {
+ new org.apache.harmony.pack200.Archive(file, out, options).pack();
+ } catch (Pack200Exception e) {
+ throw new IOException("Failed to pack Jar:" + String.valueOf(e));
+ }
+ completed(1);
+ }
+
+ public void pack(JarInputStream in, OutputStream out) throws IOException {
+ if (in == null || out == null)
+ throw new IllegalArgumentException(
+ "Must specify both input and output streams");
+ completed(0);
+ PackingOptions options = new PackingOptions();
+
+ try {
+ new org.apache.harmony.pack200.Archive(in, out, options).pack();
+ } catch (Pack200Exception e) {
+ throw new IOException("Failed to pack Jar:" + String.valueOf(e));
+ }
+ completed(1);
+ in.close();
+ }
+
+ protected void firePropertyChange(String propertyName, Object oldValue,
+ Object newValue) {
+ super.firePropertyChange(propertyName, oldValue, newValue);
+ if(newValue != null && !newValue.equals(oldValue)) {
+ if (propertyName.startsWith(CLASS_ATTRIBUTE_PFX)) {
+ String attributeName = propertyName.substring(CLASS_ATTRIBUTE_PFX.length());
+ options.addClassAttributeAction(attributeName, (String)newValue);
+ } else if (propertyName.startsWith(CODE_ATTRIBUTE_PFX)) {
+ String attributeName = propertyName.substring(CODE_ATTRIBUTE_PFX.length());
+ options.addCodeAttributeAction(attributeName, (String)newValue);
+ } else if (propertyName.equals(DEFLATE_HINT)) {
+ options.setDeflateHint((String) newValue);
+ } else if (propertyName.equals(EFFORT)) {
+ options.setEffort(Integer.parseInt((String)newValue));
+ } else if (propertyName.startsWith(FIELD_ATTRIBUTE_PFX)) {
+ String attributeName = propertyName.substring(FIELD_ATTRIBUTE_PFX.length());
+ options.addFieldAttributeAction(attributeName, (String)newValue);
+ } else if (propertyName.equals(KEEP_FILE_ORDER)) {
+ options.setKeepFileOrder(Boolean.parseBoolean((String)newValue));
+ } else if (propertyName.startsWith(METHOD_ATTRIBUTE_PFX)) {
+ String attributeName = propertyName.substring(METHOD_ATTRIBUTE_PFX.length());
+ options.addMethodAttributeAction(attributeName, (String)newValue);
+ } else if (propertyName.equals(MODIFICATION_TIME)) {
+ options.setModificationTime((String)newValue);
+ } else if (propertyName.startsWith(PASS_FILE_PFX)) {
+ if(oldValue != null && !oldValue.equals("")) {
+ options.removePassFile((String)oldValue);
+ }
+ options.addPassFile((String) newValue);
+ } else if (propertyName.equals(SEGMENT_LIMIT)) {
+ options.setSegmentLimit(Long.parseLong((String)newValue));
+ } else if (propertyName.equals(UNKNOWN_ATTRIBUTE)) {
+ options.setUnknownAttributeAction((String)newValue);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/PackingOptions.java b/src/main/java/org/apache/harmony/pack200/PackingOptions.java
new file mode 100644
index 0000000..943ca2f
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/PackingOptions.java
@@ -0,0 +1,363 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.harmony.pack200;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.objectweb.asm.Attribute;
+
+/**
+ * Utility class to manage the various options available for pack200
+ */
+public class PackingOptions {
+
+ public static final String STRIP = "strip";
+ public static final String ERROR = "error";
+ public static final String PASS = "pass";
+ public static final String KEEP = "keep";
+
+ // All options are initially set to their defaults
+ private boolean gzip = true;
+ private boolean stripDebug = false;
+ private boolean keepFileOrder = true;
+ private long segmentLimit = 1000000L;
+ private int effort = 5;
+ private String deflateHint = KEEP;
+ private String modificationTime = KEEP;
+ private List passFiles;
+ private String unknownAttributeAction = PASS;
+ private Map classAttributeActions;
+ private Map fieldAttributeActions;
+ private Map methodAttributeActions;
+ private Map codeAttributeActions;
+ private boolean verbose = false;
+ private String logFile;
+
+ private Attribute[] unknownAttributeTypes;
+
+ public boolean isGzip() {
+ return gzip;
+ }
+
+ public void setGzip(boolean gzip) {
+ this.gzip = gzip;
+ }
+
+ public boolean isStripDebug() {
+ return stripDebug;
+ }
+
+ /**
+ * Set strip debug attributes. If true, all debug attributes (i.e.
+ * LineNumberTable, SourceFile, LocalVariableTable and
+ * LocalVariableTypeTable attributes) are stripped when reading the input
+ * class files and not included in the output archive.
+ *
+ * @param stripDebug
+ */
+ public void setStripDebug(boolean stripDebug) {
+ this.stripDebug = stripDebug;
+ }
+
+ public boolean isKeepFileOrder() {
+ return keepFileOrder;
+ }
+
+ public void setKeepFileOrder(boolean keepFileOrder) {
+ this.keepFileOrder = keepFileOrder;
+ }
+
+ public long getSegmentLimit() {
+ return segmentLimit;
+ }
+
+ /**
+ * Set the segment limit (equivalent to -S command line option)
+ * @param segmentLimit - the limit in bytes
+ */
+ public void setSegmentLimit(long segmentLimit) {
+ this.segmentLimit = segmentLimit;
+ }
+
+ public int getEffort() {
+ return effort;
+ }
+
+ /**
+ * Set the compression effort level (0-9, equivalent to -E command line option)
+ * @param effort
+ */
+ public void setEffort(int effort) {
+ this.effort = effort;
+ }
+
+ public String getDeflateHint() {
+ return deflateHint;
+ }
+
+ public boolean isKeepDeflateHint() {
+ return KEEP.equals(deflateHint);
+ }
+
+ public void setDeflateHint(String deflateHint) {
+ if (!KEEP.equals(deflateHint)
+ && !"true".equals(deflateHint)
+ && !"false".equals(deflateHint)) {
+ throw new IllegalArgumentException(
+ "Bad argument: -H "
+ + deflateHint
+ + " ? deflate hint should be either true, false or keep (default)");
+ }
+ this.deflateHint = deflateHint;
+ }
+
+ public String getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(String modificationTime) {
+ if (!KEEP.equals(modificationTime)
+ && !"latest".equals(modificationTime)) {
+ throw new IllegalArgumentException(
+ "Bad argument: -m "
+ + modificationTime
+ + " ? transmit modtimes should be either latest or keep (default)");
+ }
+ this.modificationTime = modificationTime;
+ }
+
+ public boolean isPassFile(String passFileName) {
+ if (passFiles != null) {
+ for (Iterator iterator = passFiles.iterator(); iterator.hasNext();) {
+ String pass = (String) iterator.next();
+ if (passFileName.equals(pass)) {
+ return true;
+ } else if (!pass.endsWith(".class")) { // a whole directory is
+ // passed
+ if (!pass.endsWith("/")) {
+ // Make sure we don't get any false positives (e.g.
+ // exclude "org/apache/harmony/pack" should not match
+ // files under "org/apache/harmony/pack200/")
+ pass = pass + "/";
+ }
+ return passFileName.startsWith(pass);
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tell the compressor to pass the file with the given name, or if the name
+ * is a directory name all files under that directory will be passed.
+ *
+ * @param passFileName
+ * the file name
+ */
+ public void addPassFile(String passFileName) {
+ if(passFiles == null) {
+ passFiles = new ArrayList();
+ }
+ String fileSeparator = System.getProperty("file.separator");
+ if(fileSeparator.equals("\\")) {
+ // Need to escape backslashes for replaceAll(), which uses regex
+ fileSeparator += "\\";
+ }
+ passFileName = passFileName.replaceAll(fileSeparator, "/");
+ passFiles.add(passFileName);
+ }
+
+ public void removePassFile(String passFileName) {
+ passFiles.remove(passFileName);
+ }
+
+ public String getUnknownAttributeAction() {
+ return unknownAttributeAction;
+ }
+
+ /**
+ * Tell the compressor what to do if an unknown attribute is encountered
+ * @param unknownAttributeAction - the action to perform
+ */
+ public void setUnknownAttributeAction(String unknownAttributeAction) {
+ this.unknownAttributeAction = unknownAttributeAction;
+ if (!PASS.equals(unknownAttributeAction)
+ && !ERROR.equals(unknownAttributeAction)
+ && !STRIP.equals(unknownAttributeAction)) {
+ throw new RuntimeException("Incorrect option for -U, "
+ + unknownAttributeAction);
+ }
+ }
+
+ public void addClassAttributeAction(String attributeName, String action) {
+ if(classAttributeActions == null) {
+ classAttributeActions = new HashMap();
+ }
+ classAttributeActions.put(attributeName, action);
+ }
+
+ public void addFieldAttributeAction(String attributeName, String action) {
+ if(fieldAttributeActions == null) {
+ fieldAttributeActions = new HashMap();
+ }
+ fieldAttributeActions.put(attributeName, action);
... 17684 lines suppressed ...