Index: /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Pack200Exception.java =================================================================== --- /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Pack200Exception.java (revision 464089) +++ /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Pack200Exception.java (working copy) @@ -15,7 +15,9 @@ * limitations under the License. */ package org.apache.harmony.archive.internal.pack200; - +//NOTE: Do not use generics in this code; it needs to run on JVMs < 1.5 +//NOTE: Do not extract strings as messages; this code is still a work-in-progress +//NOTE: Also, don't get rid of 'else' statements for the hell of it ... /** * Represents a problem with a Pack200 coding/decoding issue. * @@ -26,7 +28,7 @@ private static final long serialVersionUID = 5168177401552611803L; - /** + /** * Create a new Pack200 exception with the given message and cause * * @param message Index: /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/AttributeLayoutMap.java =================================================================== --- /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/AttributeLayoutMap.java (revision 0) +++ /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/AttributeLayoutMap.java (revision 0) @@ -0,0 +1,107 @@ +/* + * 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.archive.internal.pack200; +//NOTE: Do not use generics in this code; it needs to run on JVMs < 1.5 +//NOTE: Do not extract strings as messages; this code is still a work-in-progress +//NOTE: Also, don't get rid of 'else' statements for the hell of it ... +import java.util.HashMap; +import java.util.Map; + +/** + * Stores a mapping from attribute names to their corresponding layout types. + * Note that names of attribute layouts and their formats are not + * internationalised, and should not be translated. + */ +public class AttributeLayoutMap { + // private static final String METADATA = "[NH[(1)]][RSHNH[RUH(1)]][TB(66,67,73,83,90)[KIH](68)[KDH](70)[KFH](74)[KJH](99)[RSH](101)[RSHRUH](115)[RUH](91)[NH[(0)]](64)[RSH[RUH(0)]]()[]]"; + + // create a whole bunch of AttributeLayouts here + private static AttributeLayout[] getDefaultAttributeLayouts() + throws Pack200Exception { + return new AttributeLayout[] { + new AttributeLayout("LineNumberTable", + AttributeLayout.CONTEXT_CODE, "NH[PHH]", 1), + new AttributeLayout("LocalVariableTable", + AttributeLayout.CONTEXT_CODE, "NH[PHOHRUHRSHH]", 2), + new AttributeLayout("LocalVariableTypeTable", + AttributeLayout.CONTEXT_CODE, "NH[PHOHRUHRSHH]", 3), + new AttributeLayout("SourceFile", + AttributeLayout.CONTEXT_CLASS, "RUNH", 17), + new AttributeLayout("ConstantValue", + AttributeLayout.CONTEXT_FIELD, "KQH", 17), + new AttributeLayout("Code", AttributeLayout.CONTEXT_METHOD, + "*", 17), + new AttributeLayout("EnclosingMethod", + AttributeLayout.CONTEXT_CLASS, "RCHRDNH", 18), + new AttributeLayout("Exceptions", + AttributeLayout.CONTEXT_METHOD, "NH[RCH]", 18), + new AttributeLayout("Signature", AttributeLayout.CONTEXT_CLASS, + "RSH", 19), + new AttributeLayout("Signature", AttributeLayout.CONTEXT_FIELD, + "RSH", 19), + new AttributeLayout("Signature", + AttributeLayout.CONTEXT_METHOD, "RSH", 19), + new AttributeLayout("Deprecated", + AttributeLayout.CONTEXT_CLASS, "", 20), + new AttributeLayout("Deprecated", + AttributeLayout.CONTEXT_FIELD, "", 20), + new AttributeLayout("Deprecated", + AttributeLayout.CONTEXT_METHOD, "", 20), + new AttributeLayout("RuntimeVisibleAnnotations", + AttributeLayout.CONTEXT_CLASS, "*", 21), + new AttributeLayout("RuntimeVisibleAnnotations", + AttributeLayout.CONTEXT_FIELD, "*", 21), + new AttributeLayout("RuntimeVisibleAnnotations", + AttributeLayout.CONTEXT_METHOD, "*", 21), + new AttributeLayout("RuntimeInvisibleAnnotations", + AttributeLayout.CONTEXT_CLASS, "*", 22), + new AttributeLayout("RuntimeInvisibleAnnotations", + AttributeLayout.CONTEXT_FIELD, "*", 22), + new AttributeLayout("RuntimeInvisibleAnnotations", + AttributeLayout.CONTEXT_METHOD, "*", 22), + new AttributeLayout("InnerClasses", + AttributeLayout.CONTEXT_CLASS, "*", 23), + new AttributeLayout("RuntimeVisibleParameterAnnotations", + AttributeLayout.CONTEXT_METHOD, "*", 23), + new AttributeLayout("class-file version", + AttributeLayout.CONTEXT_CLASS, "*", 24), + new AttributeLayout("RuntimeInvisibleParameterAnnotations", + AttributeLayout.CONTEXT_METHOD, "*", 24), + new AttributeLayout("AnnotationDefault", + AttributeLayout.CONTEXT_METHOD, "*", 25) }; + } + + private Map layouts; + + public AttributeLayoutMap() throws Pack200Exception { + this.layouts = new HashMap(); + AttributeLayout[] defaultAttributeLayouts = getDefaultAttributeLayouts(); + for (int i = 0; i < defaultAttributeLayouts.length; i++) { + add(defaultAttributeLayouts[i]); + } + } + + public void add(AttributeLayout layout) { + layouts.put(layout.key, layout); + } + + public AttributeLayout getAttributeLayout(String name, int context) + throws Pack200Exception { + return (AttributeLayout) layouts.get(new AttributeLayout.Key(name, + context)); + } +} Index: /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/AttributeLayout.java =================================================================== --- /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/AttributeLayout.java (revision 0) +++ /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/AttributeLayout.java (revision 0) @@ -0,0 +1,198 @@ +/* + * 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.archive.internal.pack200; +//NOTE: Do not use generics in this code; it needs to run on JVMs < 1.5 +//NOTE: Do not extract strings as messages; this code is still a work-in-progress +//NOTE: Also, don't get rid of 'else' statements for the hell of it ... +import org.apache.harmony.archive.internal.pack200.Segment.SegmentConstantPool; + +public class AttributeLayout { + static class Key { + public Key(String name, int context) throws Pack200Exception { + if (name == null || name.length() == 0) + throw new Pack200Exception("Cannot have an unnamed layout"); + if (context != CONTEXT_CLASS && context != CONTEXT_CODE + && context != CONTEXT_FIELD && context != CONTEXT_METHOD) + throw new Pack200Exception("Attribute context out of range: " + + context); + this.name = name; + this.context = context; + + } + + private int context; + + private String name; + + @Override + public int hashCode() { + final int PRIME = 31; + int result = 1; + result = PRIME * result + context; + result = PRIME * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final Key other = (Key) obj; + if (context != other.context) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } + + public String toString() { + return contextNames[context] + ": " + name; + } + + } + + private String layout; + + Key key; + + private long mask; + + public static final int CONTEXT_CODE = 1 << 4; + + public static final int CONTEXT_CLASS = 1 << 0; + + public static final int CONTEXT_FIELD = 1 << 2; + + public static final int CONTEXT_METHOD = 1 << 3; + + private static final String[] contextNames = { "Class", "Field", "Method", + "Code", }; + + public Codec getCodec() { + if (layout.indexOf("O") >= 0) { + return Codec.BRANCH5; + } else if (layout.indexOf("P") >= 0) { + return Codec.BCI5; + } else if (layout.indexOf("S") >= 0 && layout.indexOf("KS") < 0 + && layout.indexOf("RS") < 0) { + return Codec.SIGNED5; + } else if (layout.indexOf("B") >= 0) { + return Codec.BYTE1; + } + /* + * TODO Add this as a test (and don't commit since this is copyright + * text) && ) If the layout contains 'O', use BRANCH5. Otherwise, if the + * layout contains 'P', use BCI5. Otherwise, if the layout contains 'S' + * but not 'KS' or 'RS', use SIGNED5. Otherwise, if the layout contains + * 'B', use BYTE1. For all other layouts use UNSIGNED5. + */ + else { + return Codec.UNSIGNED5; + } + } + + public Object getValue(long value, Segment segment) throws Pack200Exception { + if (layout.startsWith("R")) { + // references + if (layout.indexOf('N') != -1) + value--; + SegmentConstantPool pool = segment.getConstantPool(); + if (layout.startsWith("RU")) { + return pool.getValue(SegmentConstantPool.UTF_8, value); + } else if (layout.startsWith("RS")) { + return pool.getValue(SegmentConstantPool.SIGNATURE, value); + } + } + throw new Pack200Exception("Unknown layout encoding: " + layout); + } + + public AttributeLayout(String name, int context, String layout, int index) + throws Pack200Exception { + super(); + this.key = new Key(name, context); + if (index >= 0) { + this.mask = 1L << index; + } else { + this.mask = 0; + } + if (layout == null) // || layout.length() == 0) + throw new Pack200Exception("Cannot have a null layout"); + this.layout = layout; + } + + public boolean isCode() { + return key.context == CONTEXT_CODE; + } + + public boolean isClass() { + return key.context == CONTEXT_CLASS; + } + + public boolean isMethod() { + return key.context == CONTEXT_METHOD; + } + + public boolean isField() { + return key.context == CONTEXT_FIELD; + } + + public int hashCode() { + return key.hashCode(); + } + + public boolean matches(long value) { + return (value & mask) != 0; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final AttributeLayout other = (AttributeLayout) obj; + if (key == null) { + if (other.key != null) + return false; + } else if (!key.equals(other.key)) + return false; + if (layout == null) { + if (other.layout != null) + return false; + } else if (!layout.equals(other.layout)) + return false; + return true; + } + + public String toString() { + return key.toString(); + } + + public String getLayout() { + return layout; + } + +} Index: /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Segment.java =================================================================== --- /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Segment.java (revision 464089) +++ /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Segment.java (working copy) @@ -15,15 +15,20 @@ * limitations under the License. */ package org.apache.harmony.archive.internal.pack200; - +//NOTE: Do not use generics in this code; it needs to run on JVMs < 1.5 +//NOTE: Do not extract strings as messages; this code is still a work-in-progress +//NOTE: Also, don't get rid of 'else' statements for the hell of it ... import java.io.ByteArrayInputStream; +import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; import java.util.zip.GZIPInputStream; -import org.apache.harmony.archive.internal.nls.Messages; +import org.apache.harmony.archive.internal.pack200.ClassFileEntry.SourceFile; /** * A Pack200 archive consists of one (or more) segments. Each segment is @@ -57,6 +62,29 @@ * @version $Revision: $ */ public class Segment { + public class SegmentConstantPool { + public static final int ALL = 0; + + public static final int SIGNATURE = 2; // TODO and more to come -- + // define in archive order + + public static final int UTF_8 = 1; + + public Object getValue(int cp, long value) throws Pack200Exception { + int index = (int)value; + if (index == -1) + return null; + if (index < 0) + throw new Pack200Exception("Cannot have a negative range"); + if (cp == UTF_8) + return cpUTF8[index]; + if (cp == SIGNATURE) + return cpSignature[index]; + // etc + throw new Error("Get value incomplete"); + } + } + /** * The magic header for a Pack200 Segment is 0xCAFED00D. I wonder where they * get their inspiration from ... @@ -106,12 +134,12 @@ throws IOException, Pack200Exception { int total = in.read(data); if (total == -1) - throw new EOFException(Messages.getString("archive.0E")); //$NON-NLS-1$ + throw new EOFException("Failed to read any data from input stream"); while (total < data.length) { int delta = in.read(data, total, data.length - total); if (delta == -1) throw new EOFException( - Messages.getString("archive.0D")); //$NON-NLS-1$ + "Failed to read some data from input stream"); total += delta; } } @@ -126,16 +154,22 @@ private String[] attributeDefinitionLayout; + private AttributeLayoutMap attributeDefinitionMap; + private String[] attributeDefinitionName; private InputStream bandHeadersInputStream; private int bandHeadersSize; + private int classAttrCount; + private int classCount; private int[] classFieldCount; + private long[] classFlags; + private String[][] classInterfaces; private int[] classMethodCount; @@ -228,7 +262,7 @@ private int innerClassCount; - private int major; + private int archiveMajor; private int methodAttrCount; @@ -236,18 +270,16 @@ private long[][] methodFlags; - private int minor; + private int archiveMinor; private int numberOfFiles; private SegmentOptions options; + private final SegmentConstantPool pool = new SegmentConstantPool(); + private int segmentsRemaining; - private int classAttrCount; - - private long[] classFlags; - /** * This is a local debugging message to aid the developer in writing this * class. It will be removed before going into production. If the property @@ -258,7 +290,7 @@ * @deprecated this should be removed from production code */ @Deprecated - private void debug(String message) { + private void debug(String message) { if (System.getProperty("debug.pack200") != null) { System.err.println(message); } @@ -423,10 +455,97 @@ } + public SegmentConstantPool getConstantPool() { + return pool; + } + public int getNumberOfFiles() { return numberOfFiles; } + /** + * Writes the segment to an output stream. The output stream should be pre-buffered for + * efficiency. Also takes the same input stream for reading, since the file bits may + * not be loaded and thus just copied from one stream to another. + * Doesn't close the output stream when finished, in case there are more entries (e.g. + * further segments) to be written. + * @param out the JarOutputStream to write data to + * @param in the same InputStream that was used to parse the segment + * @throws IOException if an error occurs whilst reading or writing to the streams + * @throws Pack200Exception if an error occurs whilst unpacking data + */ + public void writeJar(JarOutputStream out, InputStream in ) throws IOException, Pack200Exception { + processFileBits(in); + DataOutputStream dos = new DataOutputStream(out); + // out.setLevel(JarEntry.DEFLATED) + // now write the files out + int classNum = 0; + for(int i=0;i 0) - throw new Error(Messages.getString("archive.0C")); //$NON-NLS-1$ + throw new Error("No idea what the adc is for yet"); + attributeDefinitionMap = new AttributeLayoutMap(); } private void parseBcBands(InputStream in) { @@ -499,10 +619,22 @@ classAttrCount++; } if (classAttrCount > 0) - throw new Error(Messages.getString("archive.0A")); //$NON-NLS-1$ + throw new Error( + "There are attribute flags, and I don't know what to do with them"); debug("unimplemented class_attr_count"); debug("unimplemented class_attr_indexes"); debug("unimplemented class_attr_calls"); + AttributeLayout layout = attributeDefinitionMap.getAttributeLayout( + "SourceFile", AttributeLayout.CONTEXT_CLASS); + for (int i = 0; i < classCount; i++) { + long flag = classFlags[i]; + if (layout.matches(flag)) { + // we've got a value to read + long result = layout.getCodec().decode(in); + Object value = layout.getValue(result, this); + debug("Processed value " + value + " for SourceFile"); + } + } debug("unimplemented class_SourceFile_RUN"); debug("unimplemented class_EnclosingMethod_RC"); debug("unimplemented class_EnclosingMethod_RDN"); @@ -550,18 +682,6 @@ setClassCount(decodeScalar("class_count", in, Codec.UNSIGNED5)); } - private void parseCodeBands(InputStream in) { - debug("unimplemented code_headers"); - debug("unimplemented code_max_stack"); - debug("unimplemented code_max_na_locals"); - debug("unimplemented code_hander_count"); - debug("unimplemented code_hander_start_P"); - debug("unimplemented code_hander_end_PO"); - debug("unimplemented code_hander_catch_PO"); - debug("unimplemented code_hander_class_RC"); - parseCodeAttrBands(in); - } - private void parseCodeAttrBands(InputStream in) { debug("unimplemented code_flags"); debug("unimplemented code_attr_count"); @@ -582,6 +702,18 @@ } } + private void parseCodeBands(InputStream in) { + debug("unimplemented code_headers"); + debug("unimplemented code_max_stack"); + debug("unimplemented code_max_na_locals"); + debug("unimplemented code_hander_count"); + debug("unimplemented code_hander_start_P"); + debug("unimplemented code_hander_end_PO"); + debug("unimplemented code_hander_catch_PO"); + debug("unimplemented code_hander_class_RC"); + parseCodeAttrBands(in); + } + /** * Parses the constant pool class names, using {@link #cpClassCount} to * populate {@link #cpClass} from {@link #cpUTF8}. @@ -779,7 +911,7 @@ String form = cpSignatureForm[i]; int len = form.length(); StringBuffer signature = new StringBuffer(64); - ArrayList list = new ArrayList(); + ArrayList list = new ArrayList(); for (int j = 0; j < len; j++) { char c = form.charAt(j); signature.append(c); @@ -1005,38 +1137,6 @@ cpUTF8); } - private void parseMethodBands(InputStream in) throws IOException, - Pack200Exception { - methodDescr = new String[classCount][]; - for (int i = 0; i < classCount; i++) { - methodDescr[i] = parseReferences("method_descr", in, Codec.MDELTA5, - classMethodCount[i], cpDescriptor); - } - methodFlags = new long[classCount][]; - for (int i = 0; i < classCount; i++) { - methodFlags[i] = parseFlags("method_flags", in, - classMethodCount[i], Codec.UNSIGNED5, options - .hasMethodFlagsHi()); - } - for (int i = 0; i < classCount; i++) { - for (int j = 0; j < methodFlags[i].length; j++) { - long flag = methodFlags[i][j]; - if ((flag & (1 << 16)) != 0) - methodAttrCount++; - } - } - if (methodAttrCount > 0) - throw new Error( - "There are method attribute flags, and I don't know what to do with them"); - debug("unimplemented method_attr_count"); - debug("unimplemented method_attr_indexes"); - debug("unimplemented method_attr_calls"); - debug("unimplemented method_Exceptions_N"); - debug("unimplemented method_Exceptions_RC"); - debug("unimplemented method_Signature_RS"); - parseMetadataBands("method"); - } - private void parseMetadataBands(String unit) throws Pack200Exception { String[] RxA; if ("method".equals(unit)) { @@ -1073,6 +1173,38 @@ } } + private void parseMethodBands(InputStream in) throws IOException, + Pack200Exception { + methodDescr = new String[classCount][]; + for (int i = 0; i < classCount; i++) { + methodDescr[i] = parseReferences("method_descr", in, Codec.MDELTA5, + classMethodCount[i], cpDescriptor); + } + methodFlags = new long[classCount][]; + for (int i = 0; i < classCount; i++) { + methodFlags[i] = parseFlags("method_flags", in, + classMethodCount[i], Codec.UNSIGNED5, options + .hasMethodFlagsHi()); + } + for (int i = 0; i < classCount; i++) { + for (int j = 0; j < methodFlags[i].length; j++) { + long flag = methodFlags[i][j]; + if ((flag & (1 << 16)) != 0) + methodAttrCount++; + } + } + if (methodAttrCount > 0) + throw new Error( + "There are method attribute flags, and I don't know what to do with them"); + debug("unimplemented method_attr_count"); + debug("unimplemented method_attr_indexes"); + debug("unimplemented method_attr_calls"); + debug("unimplemented method_Exceptions_N"); + debug("unimplemented method_Exceptions_RC"); + debug("unimplemented method_Signature_RS"); + parseMetadataBands("method"); + } + /** * Helper method to parse count references from in, * using codec to decode the values as indexes into @@ -1107,7 +1239,7 @@ int index = decode[i]; if (index < 0 || index >= reference.length) throw new Pack200Exception( - Messages.getString("archive.06")); //$NON-NLS-1$ + "Something has gone wrong during parsing references"); result[i] = reference[index]; } return result; @@ -1130,7 +1262,7 @@ debug("-------"); parseSegmentHeader(in); if (bandHeadersSize > 0) { - byte[] bandHeaders = new byte[bandHeadersSize]; + byte[] bandHeaders = new byte[(int) bandHeadersSize]; readFully(in, bandHeaders); setBandHeadersData(bandHeaders); } @@ -1151,9 +1283,7 @@ parseClassBands(in); parseBcBands(in); // TODO Re-enable these after completing class/bytecode bands - // parseFileBands(in); - // processFileBits(in); // this just caches them in file_bits; it should - // probably start writing here? + parseFileBands(in); } private void parseSegmentHeader(InputStream in) throws IOException, @@ -1162,10 +1292,10 @@ magic.length); for (int m = 0; m < magic.length; m++) if (word[m] != magic[m]) - throw new Error(Messages.getString("archive.07")); //$NON-NLS-1$ - setMinorVersion((int) decodeScalar("archive_minver", in, + throw new Error("Bad header"); + setArchiveMinorVersion((int) decodeScalar("archive_minver", in, Codec.UNSIGNED5)); - setMajorVersion((int) decodeScalar("archive_majver", in, + setArchiveMajorVersion((int) decodeScalar("archive_majver", in, Codec.UNSIGNED5)); setOptions(new SegmentOptions((int) decodeScalar("archive_options", in, Codec.UNSIGNED5))); @@ -1283,10 +1413,10 @@ * @throws Pack200Exception * if the major version is not 150 */ - private void setMajorVersion(int version) throws Pack200Exception { + private void setArchiveMajorVersion(int version) throws Pack200Exception { if (version != 150) - throw new Pack200Exception(Messages.getString("archive.08")); //$NON-NLS-1$ - major = version; + throw new Pack200Exception("Invalid segment major version"); + archiveMajor = version; } /** @@ -1297,10 +1427,10 @@ * @throws Pack200Exception * if the minor version is not 7 */ - private void setMinorVersion(int version) throws Pack200Exception { + private void setArchiveMinorVersion(int version) throws Pack200Exception { if (version != 7) - throw new Pack200Exception(Messages.getString("archive.09")); //$NON-NLS-1$ - minor = version; + throw new Pack200Exception("Invalid segment minor version"); + archiveMinor = version; } public void setNumberOfFiles(long value) { Index: /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/RunCodec.java =================================================================== --- /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/RunCodec.java (revision 464089) +++ /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/RunCodec.java (working copy) @@ -15,12 +15,12 @@ * limitations under the License. */ package org.apache.harmony.archive.internal.pack200; - +//NOTE: Do not use generics in this code; it needs to run on JVMs < 1.5 +//NOTE: Do not extract strings as messages; this code is still a work-in-progress +//NOTE: Also, don't get rid of 'else' statements for the hell of it ... import java.io.IOException; import java.io.InputStream; -import org.apache.harmony.archive.internal.nls.Messages; - /** * A run codec is a grouping of two nested codecs; K values are decoded from * the first codec, and the remaining codes are decoded from the remaining @@ -38,32 +38,28 @@ public RunCodec(int k, Codec aCodec, Codec bCodec) throws Pack200Exception { if (k <= 0) - throw new Pack200Exception(Messages.getString("archive.12")); //$NON-NLS-1$ + throw new Pack200Exception("Cannot have a RunCodec for a negative number of numbers"); if (aCodec == null || bCodec == null) - throw new Pack200Exception(Messages.getString("archive.13")); //$NON-NLS-1$ + throw new Pack200Exception("Must supply both codecs for a RunCodec"); this.k = k; this.aCodec = aCodec; this.bCodec = bCodec; } - - @Override - public long decode(InputStream in) throws IOException, Pack200Exception { + public long decode(InputStream in) throws IOException, Pack200Exception { return decode(in,this.last); } - @Override - public long decode(InputStream in, long last) throws IOException, Pack200Exception { + public long decode(InputStream in, long last) throws IOException, Pack200Exception { if(--k>=0) { long value = aCodec.decode(in,last); this.last = (k == 0 ? 0 : value); return value; + } else { + this.last = bCodec.decode(in,last); + return this.last; } - this.last = bCodec.decode(in,last); - return this.last; } - - @Override - public String toString() { - return "RunCodec[k="+k+";aCodec="+aCodec+"bCodec="+bCodec+"]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + public String toString() { + return "RunCodec[k="+k+";aCodec="+aCodec+"bCodec="+bCodec+"]"; } } Index: /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/CodecEncoding.java =================================================================== --- /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/CodecEncoding.java (revision 464089) +++ /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/CodecEncoding.java (working copy) @@ -15,13 +15,13 @@ * limitations under the License. */ package org.apache.harmony.archive.internal.pack200; - +//NOTE: Do not use generics in this code; it needs to run on JVMs < 1.5 +//NOTE: Do not extract strings as messages; this code is still a work-in-progress +//NOTE: Also, don't get rid of 'else' statements for the hell of it ... import java.io.EOFException; import java.io.IOException; import java.io.InputStream; -import org.apache.harmony.archive.internal.nls.Messages; - public class CodecEncoding { /** * The canonical encodings are defined to allow a single byte to represent @@ -103,10 +103,10 @@ // Sanity check to make sure that no-one's been buggering with // the canonical codecs, which would really cause havoc if (canonicalCodec.length != 116) - throw new Error(Messages.getString("archive.00")); //$NON-NLS-1$ + throw new Error("Canonical encodings have been incorrectly modified"); if (value < 0) { throw new IllegalArgumentException( - Messages.getString("archive.01")); //$NON-NLS-1$ + "Encoding cannot be less than zero"); } else if (value == 0) { return defaultCodec; } else if (value <= 115) { @@ -114,13 +114,13 @@ } else if (value == 116) { int code = in.read(); if (code == -1) - throw new EOFException(Messages.getString("archive.02")); //$NON-NLS-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(Messages.getString("archive.03")); //$NON-NLS-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); @@ -132,7 +132,7 @@ 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(Messages.getString("archive.04")); //$NON-NLS-1$ + 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; @@ -165,13 +165,14 @@ // 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 uCodec = (udef ? defaultCodec : getCodec(in.read(),in,defaultCodec) ); + Codec tCodec = getCodec(in.read(),in,defaultCodec); + return new PopulationCodec(fCodec,uCodec,tCodec); } - Codec fCodec = (fdef ? defaultCodec : getCodec(in.read(),in,defaultCodec) ); - Codec uCodec = (udef ? defaultCodec : getCodec(in.read(),in,defaultCodec) ); - Codec tCodec = getCodec(in.read(),in,defaultCodec); - return new PopulationCodec(fCodec,uCodec,tCodec); } else { - throw new Pack200Exception(Messages.getString("archive.05", value)); //$NON-NLS-1$ + throw new Pack200Exception("Invalid codec encoding byte (" + value + ") found" ); } } } Index: /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/ClassFile.java =================================================================== --- /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/ClassFile.java (revision 0) +++ /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/ClassFile.java (revision 0) @@ -0,0 +1,72 @@ +/* + * 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.archive.internal.pack200; +//NOTE: Do not use generics in this code; it needs to run on JVMs < 1.5 +//NOTE: Do not extract strings as messages; this code is still a work-in-progress +//NOTE: Also, don't get rid of 'else' statements for the hell of it ... +import java.io.DataOutputStream; +import java.io.IOException; + +import org.apache.harmony.archive.internal.pack200.ClassFileEntry.Attribute; + +public class ClassFile { + int major; + int minor; + private int magic = 0xCAFEBABE; + ClassConstantPool pool = new ClassConstantPool(); + int accessFlags; + int thisClass; + int superClass; + int[] interfaces; + int[] fields; + int[] methods; + Attribute[] attributes; + public void write(DataOutputStream dos) throws IOException { + dos.writeInt(magic); + dos.writeShort(minor); + dos.writeShort(major); + dos.writeShort(pool.size()+1); + for(int i=1;i<=pool.size();i++) { + ConstantPoolEntry entry; + (entry = (ConstantPoolEntry)pool.get(i)).write(dos); + // Doubles and longs take up two spaces in the pool, but only one gets written + if (entry.getTag() == ConstantPoolEntry.CP_Double || entry.getTag() == ConstantPoolEntry.CP_Long) + i++; + }; + dos.writeShort(accessFlags); + dos.writeShort(thisClass); + dos.writeShort(superClass); + dos.writeShort(interfaces.length); + for(int i=0;i= 256 || l <=0) - throw new IllegalArgumentException(Messages.getString("archive.0F")); //$NON-NLS-1$ + throw new IllegalArgumentException("L must be between 1..255"); this.favouredCodec = favouredCodec; this.l = l; this.unvafouredCodec = unvafouredCodec; @@ -44,13 +43,13 @@ @Override public long decode(InputStream in) throws IOException, Pack200Exception { - throw new Pack200Exception(Messages.getString("archive.10")); //$NON-NLS-1$ + throw new Pack200Exception("Population encoding does not work unless the number of elements are known"); } @Override public long decode(InputStream in, long last) throws IOException, Pack200Exception { - throw new Pack200Exception(Messages.getString("archive.10")); //$NON-NLS-1$ + throw new Pack200Exception("Population encoding does not work unless the number of elements are known"); } @Override @@ -88,7 +87,7 @@ tokenCodec = codec; } if (tokenCodec == null) - throw new Pack200Exception(Messages.getString("archive.11", new Integer(k), new Integer(l))); //$NON-NLS-1$ + throw new Pack200Exception("Cannot calculate token codec from " + k + " and " + l); } } // read favourites Index: /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/ClassConstantPool.java =================================================================== --- /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/ClassConstantPool.java (revision 0) +++ /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/ClassConstantPool.java (revision 0) @@ -0,0 +1,74 @@ +/* + * 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.archive.internal.pack200; + +// NOTE: Do not use generics in this code; it needs to run on JVMs < 1.5 +// NOTE: Do not extract strings as messages; this code is still a +// work-in-progress +// NOTE: Also, don't get rid of 'else' statements for the hell of it ... +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class ClassConstantPool { + private List others = new ArrayList(); + + private List entries = new ArrayList(); + + public void add(ClassFileEntry entry) { + // TODO this should be a set - we don't want duplicates + // Only add in constant pools, but resolve all types since they may + // introduce new constant pool entries + if (entry instanceof ConstantPoolEntry) { + if (!entries.contains(entry)) + entries.add(entry); + } else { + if (!others.contains(entry)) + others.add(entry); + } + ClassFileEntry[] nestedEntries = entry.getNestedClassFileEntries(); + for (int i = 0; i < nestedEntries.length; i++) { + add(nestedEntries[i]); + } + } + + public int indexOf(ClassFileEntry entry) { + return entries.indexOf(entry) + 1; + } + + public int size() { + return entries.size(); + } + + public ClassFileEntry get(int i) { + return (ClassFileEntry) entries.get(--i); + } + + public void resolve() { + Iterator it = entries.iterator(); + while (it.hasNext()) { + ClassFileEntry entry = (ClassFileEntry) it.next(); + entry.resolve(this); + } + it = others.iterator(); + while (it.hasNext()) { + ClassFileEntry entry = (ClassFileEntry) it.next(); + entry.resolve(this); + } + } + +} Index: /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Codec.java =================================================================== --- /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Codec.java (revision 464089) +++ /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Codec.java (working copy) @@ -15,7 +15,9 @@ * limitations under the License. */ package org.apache.harmony.archive.internal.pack200; - +//NOTE: Do not use generics in this code; it needs to run on JVMs < 1.5 +//NOTE: Do not extract strings as messages; this code is still a work-in-progress +//NOTE: Also, don't get rid of 'else' statements for the hell of it ... import java.io.IOException; import java.io.InputStream; Index: /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/SegmentOptions.java =================================================================== --- /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/SegmentOptions.java (revision 464089) +++ /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/SegmentOptions.java (working copy) @@ -15,9 +15,9 @@ * limitations under the License. */ package org.apache.harmony.archive.internal.pack200; - -import org.apache.harmony.archive.internal.nls.Messages; - +//NOTE: Do not use generics in this code; it needs to run on JVMs < 1.5 +//NOTE: Do not extract strings as messages; this code is still a work-in-progress +//NOTE: Also, don't get rid of 'else' statements for the hell of it ... /** * Stores the combinations of bit flags that can be used in the segment header * options. Whilst this could be defined in {@link Segment}, it's cleaner to @@ -74,7 +74,7 @@ */ public SegmentOptions(int options) throws Pack200Exception { if ((options & UNUSED) != 0) - throw new Pack200Exception(Messages.getString("archive.14")); //$NON-NLS-1$ + throw new Pack200Exception("Some unused flags are non-zero"); this.options = options; } Index: /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/BHSDCodec.java =================================================================== --- /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/BHSDCodec.java (revision 464088) +++ /Users/alex/Documents/Harmony/Workspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/BHSDCodec.java (working copy) @@ -15,13 +15,13 @@ * limitations under the License. */ package org.apache.harmony.archive.internal.pack200; - +//NOTE: Do not use generics in this code; it needs to run on JVMs < 1.5 +//NOTE: Do not extract strings as messages; this code is still a work-in-progress +//NOTE: Also, don't get rid of 'else' statements for the hell of it ... import java.io.EOFException; import java.io.IOException; import java.io.InputStream; -import org.apache.harmony.archive.internal.nls.Messages; - /** * TODO Comment -- quite a lot can be nicked from Codec, since this was created * from it @@ -102,17 +102,17 @@ */ public BHSDCodec(int b, int h, int s, int d) { if (b < 1 || b > 5) - throw new IllegalArgumentException(Messages.getString("archive.15")); //$NON-NLS-1$ + throw new IllegalArgumentException("1<=b<=5"); if (h < 1 || h > 256) - throw new IllegalArgumentException(Messages.getString("archive.16")); //$NON-NLS-1$ + throw new IllegalArgumentException("1<=h<=256"); if (s < 0 || s > 2) - throw new IllegalArgumentException(Messages.getString("archive.17")); //$NON-NLS-1$ + throw new IllegalArgumentException("0<=s<=2"); if (d < 0 || d > 1) - throw new IllegalArgumentException(Messages.getString("archive.18")); //$NON-NLS-1$ + throw new IllegalArgumentException("0<=d<=1"); if (b == 1 && h != 256) - throw new IllegalArgumentException(Messages.getString("archive.19")); //$NON-NLS-1$ + throw new IllegalArgumentException("b=1 -> h=256"); if (h == 256 && b == 5) - throw new IllegalArgumentException(Messages.getString("archive.1A")); //$NON-NLS-1$ + throw new IllegalArgumentException("h=256 -> b!=5"); this.b = b; this.h = h; this.s = s; @@ -129,15 +129,16 @@ public long cardinality() { if (h > 1) { return (long) (l * Math.pow(1 - h, b) / (1 - h) + Math.pow(h, b)); + } else { + return (b * 255) + 1; } - return (b * 255) + 1; } @Override public long decode(InputStream in) throws IOException, Pack200Exception { if (d != 0) throw new Pack200Exception( - Messages.getString("archive.1B")); //$NON-NLS-1$ + "Delta encoding used without passing in last value; this is a coding error"); return decode(in, 0); } @@ -150,7 +151,7 @@ do { x = in.read(); if (x == -1) - throw new EOFException(Messages.getString("archive.1C")); //$NON-NLS-1$ + throw new EOFException("End of stream reached whilst decoding"); z += x * Math.pow(h, n); } while (++n < b && x >= l); // This looks more complicated than it is @@ -215,7 +216,7 @@ } else if (s == 2) { result = (3L * cardinality()) / 4 - 1; } else { - throw new Error(Messages.getString("archive.1D")); //$NON-NLS-1$ + throw new Error("Unknown s value"); } } return Math.min((s == 0 ? ((long) Integer.MAX_VALUE) << 1 @@ -243,19 +244,18 @@ * Returns the codec in the form (1,256) or (1,64,1,1). Note that trailing * zero fields are not shown. */ - @Override - public String toString() { + 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(","); //$NON-NLS-1$ + buffer.append(','); buffer.append(s); } if (d != 0) { - buffer.append(","); //$NON-NLS-1$ + buffer.append(','); buffer.append(d); } buffer.append(')');