Index: src/main/java/org/apache/harmony/pack200/AttributeLayout.java =================================================================== --- src/main/java/org/apache/harmony/pack200/AttributeLayout.java (revision 572384) +++ src/main/java/org/apache/harmony/pack200/AttributeLayout.java (working copy) @@ -20,7 +20,6 @@ // 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.pack200.Segment.SegmentConstantPool; public class AttributeLayout implements IMatcher { static class Key { @@ -111,9 +110,8 @@ private static final String[] contextNames = { "Class", "Field", "Method", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ "Code", }; //$NON-NLS-1$ - private static Object getValue(String layout, long value, Segment segment) + private static Object getValue(String layout, long value, SegmentConstantPool pool) throws Pack200Exception { - SegmentConstantPool pool = segment.getConstantPool(); if (layout.startsWith("R")) { //$NON-NLS-1$ // references if (layout.indexOf('N') != -1) @@ -210,11 +208,11 @@ return layout; } - public Object getValue(long value, Segment segment) throws Pack200Exception { - return getValue(layout, value, segment); + public Object getValue(long value, SegmentConstantPool pool) throws Pack200Exception { + return getValue(layout, value, pool); } - public Object getValue(long value, String type, Segment segment) + public Object getValue(long value, String type, SegmentConstantPool pool) throws Pack200Exception { // TODO This really needs to be better tested, esp. the different types // TODO This should have the ability to deal with RUN stuff too, and @@ -221,14 +219,14 @@ // unions if (layout.startsWith("KQ")) { //$NON-NLS-1$ if (type.equals("Ljava/lang/String;")) { //$NON-NLS-1$ - Object value2 = getValue("KS", value, segment); //$NON-NLS-1$ + Object value2 = getValue("KS", value, pool); //$NON-NLS-1$ return value2; } else { return getValue("K" + type + layout.substring(2), value, //$NON-NLS-1$ - segment); + pool); } } else { - return getValue(layout, value, segment); + return getValue(layout, value, pool); } } Index: src/main/java/org/apache/harmony/pack200/Codec.java =================================================================== --- src/main/java/org/apache/harmony/pack200/Codec.java (revision 565676) +++ src/main/java/org/apache/harmony/pack200/Codec.java (working copy) @@ -227,4 +227,32 @@ } return result; } + + /** + * Decodes a sequence of n values from in. + * + * @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 long 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 long[] decode(int n, InputStream in, long firstValue) throws IOException, + Pack200Exception { + long result[] = new long[n + 1]; + long last = 0; + for(int i=1;iint[] values + * This performs the actual work of parsing against a non-static instance of + * Segment. * - * @param name - * the name of the band (primarily for logging/debugging - * purposes) * @param in - * the InputStream to decode from - * @param defaultCodec - * the default codec for this band - * @param count - * the number of elements to read - * @return an array of decoded int[] values + * the input stream to read from * @throws IOException - * if there is a problem reading from the underlying input - * stream + * if a problem occurs during reading from the underlying stream * @throws Pack200Exception - * if there is a problem decoding the value or that the value is - * invalid + * if a problem occurs with an unexpected value or unsupported + * codec */ - private int[] decodeBandInt(String name, InputStream in, - BHSDCodec defaultCodec, int count) throws IOException, + private void parseSegment(InputStream in) throws IOException, Pack200Exception { - // TODO Might be able to improve this directly. - int[] result = new int[count]; + debug("-------"); + header = new SegmentHeader(); + header.unpack(in); + cpBands = new CpBands(this); + cpBands.unpack(in); + attrDefinitionBands = new AttrDefinitionBands(this); + attrDefinitionBands.unpack(in); + icBands = new IcBands(this); + icBands.unpack(in); + classBands = new ClassBands(this); + classBands.unpack(in); + bcBands = new BcBands(this); + bcBands.unpack(in); + fileBands = new FileBands(this); + fileBands.unpack(in); + } + + /** + * Unpacks a packed stream (either .pack. or .pack.gz) into a corresponding + * JarOuputStream. + * + * @throws Pack200Exception + * if there is a problem unpacking + * @throws IOException + * if there is a problem with I/O during unpacking + */ + public void unpack(InputStream in, JarOutputStream out) throws IOException, + Pack200Exception { + if (!in.markSupported()) + in = new BufferedInputStream(in); + // TODO Can handle multiple concatenated streams, so should deal with + // that possibility + parse(in).unpack(in, out); + } + + /** + * 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 + * 'debug.pack200' is set, this will generate messages to stderr; otherwise, + * it will be silent. + * + * @param message + * @deprecated this should be removed from production code + */ + protected void debug(String message) { + if (System.getProperty("debug.pack200") != null) { + System.err.println(message); + } + } - // TODO We need to muck around in the scenario where the first value - // read indicates - // an uber-codec - long[] longResult = decodeBandLong(name, in, defaultCodec, count); - for (int i = 0; i < count; i++) { - result[i] = (int) longResult[i]; - } - return result; - } - /** - * Decode a band and return an array of long[] values - * - * @param name - * the name of the band (primarily for logging/debugging - * purposes) - * @param in - * the InputStream to decode from - * @param codec - * the default codec for this band - * @param count - * the number of elements to read - * @return an array of decoded long[] values - * @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 - */ - private long[] decodeBandLong(String name, InputStream in, BHSDCodec codec, - int count) throws IOException, Pack200Exception { - in.mark(count * codec.getB()); - long[] result = codec.decode(count, in); - if (result.length > 0 && !codec.equals(Codec.BYTE1)) { - int first = (int) result[0]; - if (codec.isSigned() && first >= -256 && first <= -1) { - Codec weShouldHaveUsed = CodecEncoding.getCodec(-1 - first, - getBandHeadersInputStream(), codec); - in.reset(); - result = weShouldHaveUsed.decode(count, in); - } else if (!codec.isSigned() && first >= codec.getL() - && first <= codec.getL() + 255) { - Codec weShouldHaveUsed = CodecEncoding.getCodec(first - - codec.getL(), getBandHeadersInputStream(), codec); - in.reset(); - result = weShouldHaveUsed.decode(count, in); - } - } - // TODO Remove debugging code - debug("Parsed *" + name + " (" + result.length + ")"); - return result; - } /** - * Decode a scalar from the band file. A scalar is like a band, but does not - * perform any band code switching. + * 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 name - * the name of the scalar (primarily for logging/debugging - * purposes) + * @param out + * the JarOutputStream to write data to * @param in - * the input stream to read from - * @param codec - * the codec for this scalar - * @return the decoded value + * the same InputStream that was used to parse the segment * @throws IOException - * if there is a problem reading from the underlying input - * stream + * if an error occurs whilst reading or writing to the streams * @throws Pack200Exception - * if there is a problem decoding the value or that the value is - * invalid + * if an error occurs whilst unpacking data */ - private long decodeScalar(String name, InputStream in, BHSDCodec codec) + public void writeJar(JarOutputStream out, InputStream in) throws IOException, Pack200Exception { - debug("Parsed #" + name + " (1)"); - return codec.decode(in); - } - - /** - * Decode a number of scalars from the band file. A scalar is like a band, - * but does not perform any band code switching. - * - * @param name - * the name of the scalar (primarily for logging/debugging - * purposes) - * @param in - * the input stream to read from - * @param codec - * the codec for this scalar - * @return an array of decoded long[] values - * @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 - */ - private long[] decodeScalar(String name, InputStream in, BHSDCodec codec, - int n) throws IOException, Pack200Exception { - // TODO Remove debugging code - debug("Parsed #" + name + " (" + n + ")"); - return codec.decode(n, in); - } - - public long getArchiveModtime() { - return archiveModtime; - } - - public long getArchiveSize() { - return archiveSize; - } + fileBands.processFileBits(in); + DataOutputStream dos = new DataOutputStream(out); + String[] fileName = fileBands.getFileName(); + long[] fileModtime = fileBands.getFileModtime(); + long[] fileOptions = fileBands.getFileOptions(); + long[] fileSize = fileBands.getFileSize(); + byte[][] fileBits = fileBands.getFileBits(); + + // out.setLevel(JarEntry.DEFLATED) + // now write the files out + int classNum = 0; + int numberOfFiles = header.getNumberOfFiles(); + long archiveModtime = header.getArchiveModtime(); + SegmentOptions options = header.getOptions(); + for (int i = 0; i < numberOfFiles; i++) { + String name = fileName[i]; + long modtime = archiveModtime + fileModtime[i]; + boolean deflate = (fileOptions[i] & 1) == 1 + || options.shouldDeflate(); + boolean isClass = (fileOptions[i] & 2) == 2 || name == null + || name.equals(""); + if (isClass) { + // pull from headers + if (name == null || name.equals("")) + name = classBands.getClassThis()[classNum] + ".class"; + } + JarEntry entry = new JarEntry(name); + if (deflate) + entry.setMethod(JarEntry.DEFLATED); + entry.setTime(modtime); + out.putNextEntry(entry); - /** - * Obtain the band headers data as an input stream. If no band headers are - * present, this will return an empty input stream to prevent any further - * reads taking place. - * - * Note that as a stream, data consumed from this input stream can't be - * re-used. Data is only read from this stream if the encoding is such that - * additional information needs to be decoded from the stream itself. - * - * @return the band headers input stream - */ - public InputStream getBandHeadersInputStream() { - if (bandHeadersInputStream == null) { - bandHeadersInputStream = new ByteArrayInputStream(new byte[0]); + if (isClass) { + // write to dos + ClassFile classFile = buildClassFile(classNum); + classFile.write(dos); + dos.flush(); + classNum++; + } else { + long size = fileSize[i]; + entry.setSize(size); + // TODO pull from in + byte[] data = fileBits[i]; + out.write(data); + } } - return bandHeadersInputStream; - - } - - public SegmentConstantPool getConstantPool() { - return pool; - } - - public int getNumberOfFiles() { - return numberOfFiles; - } - - private SegmentOptions getOptions() { - return options; - } - - public int getSegmentsRemaining() { - return segmentsRemaining; + dos.flush(); + out.finish(); + out.flush(); } - private void parseArchiveFileCounts(InputStream in) throws IOException, - Pack200Exception { - if (getOptions().hasArchiveFileCounts()) { - setArchiveSize(decodeScalar("archive_size_hi", in, Codec.UNSIGNED5) << 32 - | decodeScalar("archive_size_lo", in, Codec.UNSIGNED5)); - setSegmentsRemaining(decodeScalar("archive_next_count", in, - Codec.UNSIGNED5)); - setArchiveModtime(decodeScalar("archive_modtime", in, - Codec.UNSIGNED5)); - setNumberOfFiles(decodeScalar("file_count", in, Codec.UNSIGNED5)); - } - } + public SegmentConstantPool getConstantPool() { + return cpBands.getConstantPool(); + } - private void parseArchiveSpecialCounts(InputStream in) throws IOException, - Pack200Exception { - if (getOptions().hasSpecialFormats()) { - setBandHeadersSize(decodeScalar("band_headers_size", in, - Codec.UNSIGNED5)); - setAttributeDefinitionCount(decodeScalar("attr_definition_count", - in, Codec.UNSIGNED5)); - } - } - /** - * Reads {@link #attributeDefinitionCount} attribute definitions from the - * stream, into {@link #attributeDefinitionHeader}, - * {@link #attributeDefinitionName} and {@link #attributeDefinitionLayout}. - * This affects the codecs that are used to parse non-standard bands. TODO - * Currently, these values if present cause a failure in the parsing. - * - * @param in - * the input stream to read from - * @throws IOException - * if a problem occurs during reading from the underlying stream - * @throws Pack200Exception - * if a problem occurs with an unexpected value or unsupported - * codec - */ - private void parseAttributeDefinition(InputStream in) throws IOException, - Pack200Exception { - attributeDefinitionHeader = decodeBandInt("attr_definition_headers", - in, Codec.BYTE1, attributeDefinitionCount); - attributeDefinitionName = parseReferences("attr_definition_name", in, - Codec.UNSIGNED5, attributeDefinitionCount, cpUTF8); - attributeDefinitionLayout = parseReferences("attr_definition_layout", - in, Codec.UNSIGNED5, attributeDefinitionCount, cpUTF8); - if (attributeDefinitionCount > 0) - throw new Error("No idea what the adc is for yet"); - attributeDefinitionMap = new AttributeLayoutMap(); - } + public SegmentHeader getSegmentHeader() { + return header; + } - /** - * @param in - * @throws Pack200Exception - * @throws IOException - */ - private void parseAttributeMethodExceptions(InputStream in) - throws Pack200Exception, IOException { - // TODO Should refactor this stuff into the layout somehow - AttributeLayout layout = attributeDefinitionMap.getAttributeLayout( - "Exceptions", AttributeLayout.CONTEXT_METHOD); - Codec codec = layout.getCodec(); - methodExceptions = new ExceptionsAttribute[classCount][]; - int[][] numExceptions = new int[classCount][]; - for (int i = 0; i < classCount; i++) { - numExceptions[i] = new int[methodFlags[i].length]; - for (int j = 0; j < methodFlags[i].length; j++) { - long flag = methodFlags[i][j]; - if (layout.matches(flag)) { - // TODO This should be a decoeBand ... - numExceptions[i][j] = (int) codec.decode(in); - } - } - } - for (int i = 0; i < classCount; i++) { - methodExceptions[i] = new ExceptionsAttribute[methodFlags[i].length]; - for (int j = 0; j < methodFlags[i].length; j++) { - long flag = methodFlags[i][j]; - int n = numExceptions[i][j]; - if (n > 0) { - CPClass[] exceptions = new CPClass[n]; - if (layout.matches(flag)) { - for (int k = 0; k < n; k++) { - long result = codec.decode(in); - exceptions[k] = new CPClass(cpClass[(int) result]); - } - } - methodExceptions[i][j] = new ExceptionsAttribute(exceptions); - methodAttributes[i][j].add(methodExceptions[i][j]); - } - } - } - } - /** - * @param in - * - */ - private void parseAttributeMethodSignature(InputStream in) - throws Pack200Exception, IOException { - parseAttributeUnknown(AttributeLayout.ATTRIBUTE_SIGNATURE, - AttributeLayout.CONTEXT_METHOD, methodFlags); - } + protected AttrDefinitionBands getAttrDefinitionBands() { + return attrDefinitionBands; + } - /** - * @param name - * @param flags - * @throws Pack200Exception - */ - private void parseAttributeUnknown(String name, int context, long[][] flags) - throws Pack200Exception { - debug("Parsing unknown attributes for " + name); - AttributeLayout layout = attributeDefinitionMap.getAttributeLayout( - name, context); - int count = SegmentUtils.countMatches(flags, layout); - if (count > 0) - throw new Error("We've got data for " + name - + " and we don't know what to do with it (yet)"); - } - private void parseBcBands(InputStream in) throws IOException, - Pack200Exception { - int bcStringRefCount = 0; - int bcInitRefCount = 0; - int bcFieldRefCount = 0; - int bcThisFieldCount = 0; - int bcMethodRefCount = 0; - int bcIMethodRefCount = 0; + protected BcBands getBcBands() { + return bcBands; + } - AttributeLayout abstractModifier = attributeDefinitionMap - .getAttributeLayout(AttributeLayout.ACC_ABSTRACT, - AttributeLayout.CONTEXT_METHOD); - AttributeLayout nativeModifier = attributeDefinitionMap - .getAttributeLayout(AttributeLayout.ACC_NATIVE, - AttributeLayout.CONTEXT_METHOD); - AttributeLayout staticModifier = attributeDefinitionMap - .getAttributeLayout(AttributeLayout.ACC_STATIC, - AttributeLayout.CONTEXT_METHOD); - methodByteCodePacked = new byte[classCount][][]; - int bcParsed = 0; - for (int c = 0; c < classCount; c++) { - int numberOfMethods = methodFlags[c].length; - methodByteCodePacked[c] = new byte[numberOfMethods][]; - for (int m = 0; m < numberOfMethods; m++) { - long methodFlag = methodFlags[c][m]; - if (!abstractModifier.matches(methodFlag) - && !nativeModifier.matches(methodFlag)) { - ByteArrayOutputStream codeBytes = new ByteArrayOutputStream(); - byte code; - while ((code = (byte) (0xff & in.read())) != -1) - codeBytes.write(code); - methodByteCodePacked[c][m] = codeBytes.toByteArray(); - bcParsed += methodByteCodePacked[c][m].length; - for (int i = 0; i < methodByteCodePacked[c][m].length; i++) { - int codePacked = 0xff & methodByteCodePacked[c][m][i]; - // TODO a lot of this needs to be encapsulated in the - // place that - // calculates what the arguments are, since (a) it will - // need - // to know where to get them, and (b) what to do with - // them - // once they've been gotten. But that's for another - // time. - switch (codePacked) { - case 18: // (a)ldc - bcStringRefCount++; - break; - case 178: // getstatic - case 179: // putstatic - case 180: // getfield - case 181: // putfield - bcFieldRefCount++; - break; - case 182: // invokevirtual - case 183: // invokespecial - case 184: // invokestatic - bcMethodRefCount++; - break; - case 185: // invokeinterface - bcIMethodRefCount++; - break; - case 202: // getstatic_this - case 203: // putstatic_this - case 204: // getfield_this - case 205: // putfield_this - bcThisFieldCount++; - break; - case 231: // invoke_special_init - bcInitRefCount++; - break; - default: // unhandled specifically at this stage - debug("Found unhandled " - + ByteCode.getByteCode(codePacked)); - } - } - } - } - } - // other bytecode bands - debug("Parsed *bc_codes (" + bcParsed + ")"); - debug("unimplemented bc_case_count"); - debug("unimplemented bc_case_value"); - debug("unimplemented bc_byte"); - debug("unimplemented bc_short"); - debug("unimplemented bc_local"); - debug("unimplemented bc_label"); - debug("unimplemented bc_intref"); - debug("unimplemented bc_floatref"); - debug("unimplemented bc_longref"); - debug("unimplemented bc_doubleref"); - int[] bcStringRef = decodeBandInt("bc_stringref", in, Codec.DELTA5, - bcStringRefCount); - debug("unimplemented bc_classref"); - int[] bcFieldRef = decodeBandInt("bc_fieldref", in, Codec.DELTA5, - bcFieldRefCount); - int[] bcMethodRef = decodeBandInt("bc_methodref", in, Codec.UNSIGNED5, - bcMethodRefCount); - int[] bcIMethodRef = decodeBandInt("bc_imethodref", in, Codec.DELTA5, - bcIMethodRefCount); - int[] bcThisField = decodeBandInt("bc_thisfield", in, Codec.UNSIGNED5, - bcThisFieldCount); - debug("unimplemented bc_superfield"); - debug("unimplemented bc_thismethod"); - debug("unimplemented bc_supermethod"); - debug("unimplemented bc_initref"); - int[] bcInitRef = decodeBandInt("bc_initref", in, Codec.UNSIGNED5, - bcInitRefCount); - debug("unimplemented bc_escref"); - debug("unimplemented bc_escrefsize"); - debug("unimplemented bc_escsize"); - debug("unimplemented bc_escbyte"); - int i = 0; - for (int c = 0; c < classCount; c++) { - int numberOfMethods = methodFlags[c].length; - for (int m = 0; m < numberOfMethods; m++) { - long methodFlag = methodFlags[c][m]; - if (!abstractModifier.matches(methodFlag) - && !nativeModifier.matches(methodFlag)) { - int maxStack = codeMaxStack[i]; - int maxLocal = codeMaxNALocals[i]; - if (!staticModifier.matches(methodFlag)) - maxLocal++; // one for 'this' parameter - maxLocal += SegmentUtils.countArgs(methodDescr[c][m]); - // TODO Move creation of code attribute until after constant - // pool resolved - CodeAttribute attr = new CodeAttribute(maxStack, maxLocal, - methodByteCodePacked[c][m]); - methodAttributes[c][m].add(attr); - i++; - } - } - } + protected ClassBands getClassBands() { + return classBands; + } - } - private void parseClassAttrBands(InputStream in) throws IOException, - Pack200Exception { - classFlags = parseFlags("class_flags", in, classCount, Codec.UNSIGNED5, - options.hasClassFlagsHi()); - for (int i = 0; i < classCount; i++) { - long flag = classFlags[i]; - if ((flag & (1 << 16)) != 0) - classAttrCount++; - } - if (classAttrCount > 0) - 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( - AttributeLayout.ATTRIBUTE_SOURCE_FILE, - 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 - // TODO File this as a sourcefile attribute and don't generate - // everything below - long result = layout.getCodec().decode(in); - Object value = layout.getValue(result, this); - debug("Processed value " + value + " for SourceFile"); - } - } - debug("unimplemented class_EnclosingMethod_RC"); - debug("unimplemented class_EnclosingMethod_RDN"); - debug("unimplemented class_Signature_RS"); - parseMetadataBands(AttributeLayout.CONTEXT_CLASS); - debug("unimplemented class_InnerClasses_N"); - debug("unimplemented class_InnerClasses_RC"); - debug("unimplemented class_InnerClasses_F"); - debug("unimplemented class_InnerClasses_outer_RCN"); - debug("unimplemented class_InnerClasses_inner_RCN"); - debug("unimplemented class_file_version_minor_H"); - debug("unimplemented class_file_version_major_H"); - } + protected CpBands getCpBands() { + return cpBands; + } - private void parseClassBands(InputStream in) throws IOException, - Pack200Exception { - classThis = parseReferences("class_this", in, Codec.DELTA5, classCount, - cpClass); - classSuper = parseReferences("class_super", in, Codec.DELTA5, - classCount, cpClass); - classInterfaces = new String[classCount][]; - int[] classInterfaceLengths = decodeBandInt("class_interface_count", - in, Codec.DELTA5, classCount); - classInterfaces = parseReferences("class_interface", in, Codec.DELTA5, - classCount, classInterfaceLengths, cpClass); - classFieldCount = decodeBandInt("class_field_count", in, Codec.DELTA5, - classCount); - classMethodCount = decodeBandInt("class_method_count", in, - Codec.DELTA5, classCount); - parseFieldBands(in); - parseMethodBands(in); - parseClassAttrBands(in); - parseCodeBands(in); - } - private void parseClassCounts(InputStream in) throws IOException, - Pack200Exception { - setInnerClassCount(decodeScalar("ic_count", in, Codec.UNSIGNED5)); - setDefaultClassMinorVersion(decodeScalar("default_class_minver", in, - Codec.UNSIGNED5)); - setDefaultClassMajorVersion(decodeScalar("default_class_majver", in, - Codec.UNSIGNED5)); - setClassCount(decodeScalar("class_count", in, Codec.UNSIGNED5)); - } + protected FileBands getFileBands() { + return fileBands; + } - private void parseCodeAttrBands(InputStream in) { - debug("unimplemented code_flags"); - debug("unimplemented code_attr_count"); - debug("unimplemented code_attr_indexes"); - debug("unimplemented code_attr_calls"); - debug("unimplemented code_LineNumberTable_N"); - debug("unimplemented code_LineNumberTable_bci_P"); - debug("unimplemented code_LineNumberTable_line"); - String[] types = { "LocalVariableTable", "LocalVariableTypeTable" }; - for (int i = 0; i < types.length; i++) { - String type = types[i]; - debug("unimplemented code_" + type + "_N"); - debug("unimplemented code_" + type + "_bci_P"); - debug("unimplemented code_" + type + "_span_O"); - debug("unimplemented code_" + type + "_name_RU"); - debug("unimplemented code_" + type + "_type_RS"); - debug("unimplemented code_" + type + "_slot"); - } - } - private void parseCodeBands(InputStream in) throws Pack200Exception, - IOException { - AttributeLayout layout = attributeDefinitionMap.getAttributeLayout( - AttributeLayout.ATTRIBUTE_CODE, AttributeLayout.CONTEXT_METHOD); + protected SegmentHeader getHeader() { + return header; + } - int codeBands = SegmentUtils.countMatches(methodFlags, layout); - int[] codeHeaders = decodeBandInt("code_headers", in, Codec.BYTE1, - codeBands); - int codeSpecialHeader = 0; - for (int i = 0; i < codeBands; i++) { - if (codeHeaders[i] == 0) - codeSpecialHeader++; - } - int[] codeMaxStackSpecials = decodeBandInt("code_max_stack", in, - Codec.UNSIGNED5, codeSpecialHeader); - int[] codeMaxNALocalsSpecials = decodeBandInt("code_max_na_locals", in, - Codec.UNSIGNED5, codeSpecialHeader); - int[] codeHandlerCountSpecials = decodeBandInt("code_handler_count", - in, Codec.UNSIGNED5, codeSpecialHeader); - codeMaxStack = new int[codeBands]; - codeMaxNALocals = new int[codeBands]; - codeHandlerCount = new int[codeBands]; - int special = 0; - for (int i = 0; i < codeBands; i++) { - int header = 0xff & codeHeaders[i]; - if (header < 0) { - throw new IllegalStateException("Shouldn't get here"); - } else if (header == 0) { - codeMaxStack[i] = codeMaxStackSpecials[special]; - codeMaxNALocals[i] = codeMaxNALocalsSpecials[special]; - codeHandlerCount[i] = codeHandlerCountSpecials[special]; - special++; - } else if (header <= 144) { - codeMaxStack[i] = (header - 1) % 12; - codeMaxNALocals[i] = (header - 1) / 12; - codeHandlerCount[i] = 0; - } else if (header <= 208) { - codeMaxStack[i] = (header - 145) % 8; - codeMaxNALocals[i] = (header - 145) / 8; - codeHandlerCount[i] = 1; - } else if (header <= 255) { - codeMaxStack[i] = (header - 209) % 7; - codeMaxNALocals[i] = (header - 209) / 7; - codeHandlerCount[i] = 2; - } else { - throw new IllegalStateException("Shouldn't get here either"); - } - } - 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); - } + protected IcBands getIcBands() { + return icBands; + } - /** - * Parses the constant pool class names, using {@link #cpClassCount} to - * populate {@link #cpClass} from {@link #cpUTF8}. - * - * @param in - * the input stream to read from - * @throws IOException - * if a problem occurs during reading from the underlying stream - * @throws Pack200Exception - * if a problem occurs with an unexpected value or unsupported - * codec - */ - private void parseCpClass(InputStream in) throws IOException, - Pack200Exception { - cpClass = parseReferences("cp_Class", in, Codec.UDELTA5, cpClassCount, - cpUTF8); - } - - private void parseCpCounts(InputStream in) throws IOException, - Pack200Exception { - setCPUtf8Count(decodeScalar("cp_Utf8_count", in, Codec.UNSIGNED5)); - if (getOptions().hasCPNumberCounts()) { - setCPIntCount(decodeScalar("cp_Int_count", in, Codec.UNSIGNED5)); - setCPFloatCount(decodeScalar("cp_Float_count", in, Codec.UNSIGNED5)); - setCPLongCount(decodeScalar("cp_Long_count", in, Codec.UNSIGNED5)); - setCPDoubleCount(decodeScalar("cp_Double_count", in, - Codec.UNSIGNED5)); - } - setCPStringCount(decodeScalar("cp_String_count", in, Codec.UNSIGNED5)); - setCPClassCount(decodeScalar("cp_Class_count", in, Codec.UNSIGNED5)); - setCPSignatureCount(decodeScalar("cp_Signature_count", in, - Codec.UNSIGNED5)); - setCPDescriptorCount(decodeScalar("cp_Descr_count", in, Codec.UNSIGNED5)); - setCPFieldCount(decodeScalar("cp_Field_count", in, Codec.UNSIGNED5)); - setCPMethodCount(decodeScalar("cp_Method_count", in, Codec.UNSIGNED5)); - setCPIMethodCount(decodeScalar("cp_Imethod_count", in, Codec.UNSIGNED5)); - } - - /** - * Parses the constant pool descriptor definitions, using - * {@link #cpDescriptorCount} to populate {@link #cpDescriptor}. For ease - * of use, the cpDescriptor is stored as a string of the form name:type, - * largely to make it easier for representing field and method descriptors - * (e.g. out:java.lang.PrintStream) in a way that is - * compatible with passing String arrays. - * - * @param in - * the input stream to read from - * @throws IOException - * if a problem occurs during reading from the underlying stream - * @throws Pack200Exception - * if a problem occurs with an unexpected value or unsupported - * codec - */ - private void parseCpDescriptor(InputStream in) throws IOException, - Pack200Exception { - String[] cpDescriptorNames = parseReferences("cp_Descr_name", in, - Codec.DELTA5, cpDescriptorCount, cpUTF8); - String[] cpDescriptorTypes = parseReferences("cp_Descr_type", in, - Codec.UDELTA5, cpDescriptorCount, cpSignature); - cpDescriptor = new String[cpDescriptorCount]; - for (int i = 0; i < cpDescriptorCount; i++) { - cpDescriptor[i] = cpDescriptorNames[i] + ":" + cpDescriptorTypes[i]; //$NON-NLS-1$ - } - } - - private void parseCpDouble(InputStream in) throws IOException, - Pack200Exception { - cpDouble = new double[cpDoubleCount]; - long[] hiBits = decodeBandLong("cp_Double_hi", in, Codec.UDELTA5, - cpDoubleCount); - long[] loBits = decodeBandLong("cp_Double_lo", in, Codec.DELTA5, - cpDoubleCount); - for (int i = 0; i < cpDoubleCount; i++) { - cpDouble[i] = Double.longBitsToDouble(hiBits[i] << 32 | loBits[i]); - } - } - - /** - * Parses the constant pool field definitions, using {@link #cpFieldCount} - * to populate {@link #cpFieldClass} and {@link #cpFieldDescriptor}. - * - * @param in - * the input stream to read from - * @throws IOException - * if a problem occurs during reading from the underlying stream - * @throws Pack200Exception - * if a problem occurs with an unexpected value or unsupported - * codec - */ - private void parseCpField(InputStream in) throws IOException, - Pack200Exception { - cpFieldClass = parseReferences("cp_Field_class", in, Codec.DELTA5, - cpFieldCount, cpClass); - cpFieldDescriptor = parseReferences("cp_Field_desc", in, Codec.UDELTA5, - cpFieldCount, cpDescriptor); - } - - private void parseCpFloat(InputStream in) throws IOException, - Pack200Exception { - cpFloat = new float[cpFloatCount]; - int floatBits[] = decodeBandInt("cp_Float", in, Codec.UDELTA5, - cpFloatCount); - for (int i = 0; i < cpFloatCount; i++) { - cpFloat[i] = Float.intBitsToFloat(floatBits[i]); - } - } - - /** - * Parses the constant pool interface method definitions, using - * {@link #cpIMethodCount} to populate {@link #cpIMethodClass} and - * {@link #cpIMethodDescriptor}. - * - * @param in - * the input stream to read from - * @throws IOException - * if a problem occurs during reading from the underlying stream - * @throws Pack200Exception - * if a problem occurs with an unexpected value or unsupported - * codec - */ - private void parseCpIMethod(InputStream in) throws IOException, - Pack200Exception { - cpIMethodClass = parseReferences("cp_Imethod_class", in, Codec.DELTA5, - cpIMethodCount, cpClass); - cpIMethodDescriptor = parseReferences("cp_Imethod_desc", in, - Codec.UDELTA5, cpIMethodCount, cpDescriptor); - } - - private void parseCpInt(InputStream in) throws IOException, - Pack200Exception { - cpInt = new int[cpIntCount]; - long last = 0; - for (int i = 0; i < cpIntCount; i++) { - last = Codec.UDELTA5.decode(in, last); - cpInt[i] = (int) last; - } - } - - private void parseCpLong(InputStream in) throws IOException, - Pack200Exception { - cpLong = parseFlags("cp_Long", in, cpLongCount, new int[] { 1 }, - Codec.UDELTA5, Codec.DELTA5)[0]; - } - - /** - * Parses the constant pool method definitions, using {@link #cpMethodCount} - * to populate {@link #cpMethodClass} and {@link #cpMethodDescriptor}. - * - * @param in - * the input stream to read from - * @throws IOException - * if a problem occurs during reading from the underlying stream - * @throws Pack200Exception - * if a problem occurs with an unexpected value or unsupported - * codec - */ - private void parseCpMethod(InputStream in) throws IOException, - Pack200Exception { - cpMethodClass = parseReferences("cp_Method_class", in, Codec.DELTA5, - cpMethodCount, cpClass); - cpMethodDescriptor = parseReferences("cp_Method_desc", in, - Codec.UDELTA5, cpMethodCount, cpDescriptor); - } - - /** - * Parses the constant pool signature classes, using - * {@link #cpSignatureCount} to populate {@link #cpSignature}. A signature - * form is akin to the bytecode representation of a class; Z for boolean, I - * for int, [ for array etc. However, although classes are started with L, - * the classname does not follow the form; instead, there is a separate - * array of classes. So an array corresponding to - * public static void main(String args[]) has a form of - * [L(V) and a classes array of - * [java.lang.String]. The {@link #cpSignature} is a string - * representation identical to the bytecode equivalent - * [Ljava/lang/String;(V) TODO Check that the form is as - * above and update other types e.g. J - * - * @param in - * the input stream to read from - * @throws IOException - * if a problem occurs during reading from the underlying stream - * @throws Pack200Exception - * if a problem occurs with an unexpected value or unsupported - * codec - */ - private void parseCpSignature(InputStream in) throws IOException, - Pack200Exception { - String[] cpSignatureForm = parseReferences("cp_Signature_form", in, - Codec.DELTA5, cpSignatureCount, cpUTF8); - cpSignature = new String[cpSignatureCount]; - long last = 0; - for (int i = 0; i < cpSignatureCount; i++) { - String form = cpSignatureForm[i]; - int len = form.length(); - StringBuffer signature = new StringBuffer(64); - ArrayList list = new ArrayList(); - for (int j = 0; j < len; j++) { - char c = form.charAt(j); - signature.append(c); - if (c == 'L') { - int index = (int) (last = Codec.UDELTA5.decode(in, last)); - String className = cpClass[index]; - list.add(className); - signature.append(className); - } - } - cpSignature[i] = signature.toString(); - } - } - - /** - * Parses the constant pool strings, using {@link #cpStringCount} to - * populate {@link #cpString} from indexes into {@link #cpUTF8}. - * - * @param in - * the input stream to read from - * @throws IOException - * if a problem occurs during reading from the underlying stream - * @throws Pack200Exception - * if a problem occurs with an unexpected value or unsupported - * codec - */ - private void parseCpString(InputStream in) throws IOException, - Pack200Exception { - cpString = new String[cpStringCount]; - long last = 0; - for (int i = 0; i < cpStringCount; i++) { - int index = (int) (last = Codec.UDELTA5.decode(in, last)); - cpString[i] = cpUTF8[index]; - } - } - - private void parseCpUtf8(InputStream in) throws IOException, - Pack200Exception { - // TODO Update codec.decode -> decodeScalar - cpUTF8 = new String[cpUTF8Count]; - cpUTF8[0] = ""; //$NON-NLS-1$ - int[] prefix = new int[cpUTF8Count]; - int[] suffix = new int[cpUTF8Count]; - if (cpUTF8Count > 0) { - prefix[0] = 0; - suffix[0] = 0; - if (cpUTF8Count > 1) - prefix[1] = 0; - } - long last = 0; - for (int i = 2; i < cpUTF8Count; i++) { - last = prefix[i] = (int) Codec.DELTA5.decode(in, last); - } - int chars = 0; - int bigSuffix = 0; - for (int i = 1; i < cpUTF8Count; i++) { - last = suffix[i] = (int) Codec.UNSIGNED5.decode(in); - if (last == 0) { - bigSuffix++; - } else { - chars += last; - } - } - char data[] = new char[chars]; - for (int i = 0; i < data.length; i++) { - data[i] = (char) Codec.CHAR3.decode(in); - } - // read in the big suffix data - char bigSuffixData[][] = new char[bigSuffix][]; - last = 0; - for (int i = 0; i < bigSuffix; i++) { - last = (int) Codec.DELTA5.decode(in, last); - bigSuffixData[i] = new char[(int) last]; - } - // initialize big suffix data - for (int i = 0; i < bigSuffix; i++) { - char[] singleBigSuffixData = bigSuffixData[i]; - last = 0; - for (int j = 0; j < singleBigSuffixData.length; j++) { - last = singleBigSuffixData[j] = (char) Codec.DELTA5.decode(in, - last); - } - } - // go through the strings - chars = 0; - bigSuffix = 0; - for (int i = 1; i < cpUTF8Count; i++) { - String lastString = cpUTF8[i - 1]; - if (suffix[i] == 0) { - // The big suffix stuff hasn't been tested, and I'll be - // surprised if it works first time w/o errors ... - cpUTF8[i] = lastString.substring(0, prefix[i]) - + new String(bigSuffixData[bigSuffix++]); - } else { - cpUTF8[i] = lastString.substring(0, prefix[i]) - + new String(data, chars, suffix[i]); - chars += suffix[i]; - } - } - } - - private void parseFieldBands(InputStream in) throws IOException, - Pack200Exception { - fieldDescr = parseReferences("field_descr", in, Codec.DELTA5, - classCount, classFieldCount, cpDescriptor); - fieldFlags = parseFlags("field_flags", in, classCount, classFieldCount, - Codec.UNSIGNED5, options.hasFieldFlagsHi()); - for (int i = 0; i < classCount; i++) { - for (int j = 0; j < fieldFlags[i].length; j++) { - long flag = fieldFlags[i][j]; - if ((flag & (1 << 16)) != 0) - fieldAttrCount++; - } - } - if (fieldAttrCount > 0) - throw new Error( - "There are attribute flags, and I don't know what to do with them"); - debug("unimplemented field_attr_indexes"); - debug("unimplemented field_attr_calls"); - AttributeLayout layout = attributeDefinitionMap.getAttributeLayout( - "ConstantValue", AttributeLayout.CONTEXT_FIELD); - Codec codec = layout.getCodec(); - fieldAttributes = new ArrayList[classCount][]; - for (int i = 0; i < classCount; i++) { - fieldAttributes[i] = new ArrayList[fieldFlags[i].length]; - for (int j = 0; j < fieldFlags[i].length; j++) { - fieldAttributes[i][j] = new ArrayList(); - long flag = fieldFlags[i][j]; - if (layout.matches(flag)) { - // we've got a value to read - long result = codec.decode(in); - String desc = fieldDescr[i][j]; - int colon = desc.indexOf(':'); - // String name = desc.substring(0, colon); - String type = desc.substring(colon + 1); - // TODO Got to get better at this ... in any case, it should - // be e.g. KIB or KIH - if (type.equals("B") || type.equals("H")) - type = "I"; - Object value = layout.getValue(result, type, this); - fieldAttributes[i][j] - .add(new ConstantValueAttribute(value)); - debug("Processed value " + value + " for ConstantValue"); - } - } - } - debug("unimplemented field_Signature_RS"); - parseMetadataBands(AttributeLayout.CONTEXT_FIELD); - } - - /** - * Parses the file band headers (not including the actual bits themselves). - * At the end of this parse call, the input stream will be positioned at the - * start of the file_bits themselves, and there will be Sum(file_size) bits - * remaining in the stream with BYTE1 compression. A decent implementation - * will probably just stream the bytes out to the reconstituted Jar rather - * than caching them. - * - * @param in - * the input stream to read from - * @throws IOException - * if a problem occurs during reading from the underlying stream - * @throws Pack200Exception - * if a problem occurs with an unexpected value or unsupported - * codec - */ - private void parseFileBands(InputStream in) throws IOException, - Pack200Exception { - // if (false && System.getProperty("debug.pack200") != null) { - // // TODO HACK - // fileSize = new long[numberOfFiles]; - // fileModtime = new long[numberOfFiles]; - // fileOptions = new long[numberOfFiles]; - // fileName = new String[numberOfFiles]; - // Arrays.fill(fileName, ""); - // return; - // } - long last; - fileName = parseReferences("file_name", in, Codec.UNSIGNED5, - numberOfFiles, cpUTF8); - fileSize = new long[numberOfFiles]; - if (options.hasFileSizeHi()) { - last = 0; - for (int i = 0; i < numberOfFiles; i++) { - fileSize[i] = (last = Codec.UNSIGNED5.decode(in, last)) << 32; - } - } - last = 0; - for (int i = 0; i < numberOfFiles; i++) { - fileSize[i] |= (last = Codec.UNSIGNED5.decode(in, last)); - } - fileModtime = new long[numberOfFiles]; - if (options.hasFileModtime()) { - last = 0; - for (int i = 0; i < numberOfFiles; i++) { - fileModtime[i] |= (last = Codec.DELTA5.decode(in, last)); - } - } - fileOptions = new long[numberOfFiles]; - if (options.hasFileOptions()) { - last = 0; - for (int i = 0; i < numberOfFiles; i++) { - fileOptions[i] |= (last = Codec.UNSIGNED5.decode(in, last)); - } - } - } - - private long[] parseFlags(String name, InputStream in, int count, - Codec codec, boolean hasHi) throws IOException, Pack200Exception { - return parseFlags(name, in, 1, new int[] { count }, (hasHi ? codec - : null), codec)[0]; - } - - private long[][] parseFlags(String name, InputStream in, int count, - int counts[], Codec codec, boolean hasHi) throws IOException, - Pack200Exception { - return parseFlags(name, in, count, counts, (hasHi ? codec : null), - codec); - } - - private long[][] parseFlags(String name, InputStream in, int count, - int counts[], Codec hiCodec, Codec loCodec) throws IOException, - Pack200Exception { - // TODO Move away from decoding into a parseBand type structure - if (count == 0) { - return new long[][] { {} }; - } - long[][] result = new long[count][]; - // TODO What happens when the decode here indicates a different - // encoding? - // TODO Move this to a decodeBandInt - long last = 0; - for (int j = 0; j < count; j++) { - result[j] = new long[counts[j]]; - for (int i = 0; i < counts[j] && hiCodec != null; i++) { - last = hiCodec.decode(in, last); - result[j][i] = last << 32; - } - } - last = 0; - for (int j = 0; j < count; j++) - for (int i = 0; i < counts[j]; i++) { - last = loCodec.decode(in, last); - result[j][i] = result[j][i] | last; - } - // TODO Remove debugging code - debug("Parsed *" + name + " (" + result.length + ")"); - return result; - } - - private void parseIcBands(InputStream in) throws IOException, - Pack200Exception { - icThisClass = parseReferences("ic_this_class", in, Codec.UDELTA5, - innerClassCount, cpClass); - icFlags = new int[innerClassCount]; - long last = 0; - int outerClasses = 0; - // ic_flags - for (int i = 0; i < innerClassCount; i++) { - icFlags[i] = (int) (last = Codec.UNSIGNED5.decode(in, last)); - if ((icFlags[i] & 1 << 16) != 0) - outerClasses++; - } - icOuterClass = parseReferences("ic_outer_class", in, Codec.DELTA5, - outerClasses, cpClass); - icName = parseReferences("ic_name", in, Codec.DELTA5, outerClasses, - cpUTF8); - } - - private void parseMetadataBands(int context) throws Pack200Exception { - String[] RxA; - if (AttributeLayout.CONTEXT_METHOD == context) { - RxA = new String[] { "RVA", "RIA", "RVPA", "RIPA", "AD" }; - } else if (AttributeLayout.CONTEXT_FIELD == context - || AttributeLayout.CONTEXT_CLASS == context) { - RxA = new String[] { "RVA", "RIA" }; - } else { - throw new Pack200Exception("Unknown type of metadata unit " - + context); - } - // AttributeLayout layout = - // map.get(RuntimeVisibleAnnotations,class/field/method as int) - // foreachheader ... - // if layout.matches(header[n] or whatever) - String contextName = (AttributeLayout.CONTEXT_METHOD == context ? "method" - : (AttributeLayout.CONTEXT_FIELD == context ? "field" - : (AttributeLayout.CONTEXT_CLASS == context ? "class" - : "unkowon"))); - for (int i = 0; i < RxA.length; i++) { - String rxa = RxA[i]; - if (rxa.indexOf("P") >= 0) { - debug("unimplemented " + contextName + "_" + rxa + "_param_NB"); - } - if (!rxa.equals("AD")) { - debug("unimplemented " + contextName + "_" + rxa + "_anno_N"); - debug("unimplemented " + contextName + "_" + rxa + "_type_RS"); - debug("unimplemented " + contextName + "_" + rxa + "_pair_N"); - debug("unimplemented " + contextName + "_" + rxa + "_name_RU"); - } - debug("unimplemented " + contextName + "_" + rxa + "_T"); - debug("unimplemented " + contextName + "_" + rxa + "_caseI_KI"); - debug("unimplemented " + contextName + "_" + rxa + "_caseD_KD"); - debug("unimplemented " + contextName + "_" + rxa + "_caseF_KF"); - debug("unimplemented " + contextName + "_" + rxa + "_caseJ_KJ"); - debug("unimplemented " + contextName + "_" + rxa + "_casec_RS"); - debug("unimplemented " + contextName + "_" + rxa + "_caseet_RS"); - debug("unimplemented " + contextName + "_" + rxa + "_caseec_RU"); - debug("unimplemented " + contextName + "_" + rxa + "_cases_RU"); - debug("unimplemented " + contextName + "_" + rxa + "_casearray_N"); - debug("unimplemented " + contextName + "_" + rxa + "_nesttype_RS"); - debug("unimplemented " + contextName + "_" + rxa + "_nestpair_N"); - debug("unimplemented " + contextName + "_" + rxa + "_nestname_RU"); - } - } - - private void parseMethodBands(InputStream in) throws IOException, - Pack200Exception { - methodDescr = parseReferences("method_descr", in, Codec.MDELTA5, - classCount, classMethodCount, cpDescriptor); - methodFlags = parseFlags("method_flags", in, classCount, - classMethodCount, 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"); - // assign empty method attributes - methodAttributes = new ArrayList[classCount][]; - for (int i = 0; i < classCount; i++) { - methodAttributes[i] = new ArrayList[methodFlags[i].length]; - for (int j = 0; j < methodFlags[i].length; j++) { - methodAttributes[i][j] = new ArrayList(); - } - } - parseAttributeMethodExceptions(in); - parseAttributeMethodSignature(in); - parseMetadataBands(AttributeLayout.CONTEXT_METHOD); - } - - /** - * Helper method to parse count references from in, - * using codec to decode the values as indexes into - * reference (which is populated prior to this call). An - * exception is thrown if a decoded index falls outside the range - * [0..reference.length-1]. Unlike the other parseReferences, this - * post-processes the result into an array of results. - * - * @param name - * TODO - * @param in - * the input stream to read from - * @param codec - * the codec to use for decoding - * @param count - * the number of references to decode - * @param reference - * the array of values to use for the indexes; often - * {@link #cpUTF8} - * - * @throws IOException - * if a problem occurs during reading from the underlying stream - * @throws Pack200Exception - * if a problem occurs with an unexpected value or unsupported - * codec - */ - private String[][] parseReferences(String name, InputStream in, - BHSDCodec codec, int count, int counts[], String[] reference) - throws IOException, Pack200Exception { - if (count == 0) { - return new String[][] { {} }; - } - String[][] result = new String[count][]; - int sum = 0; - for (int i = 0; i < count; i++) { - result[i] = new String[counts[i]]; - sum += counts[i]; - } - // TODO Merge the decode and parsing of a multiple structure into one - String[] result1 = new String[sum]; - int[] decode = decodeBandInt(name, in, codec, sum); - for (int i1 = 0; i1 < sum; i1++) { - int index = decode[i1]; - if (index < 0 || index >= reference.length) - throw new Pack200Exception( - "Something has gone wrong during parsing references"); - result1[i1] = reference[index]; - } - String[] refs = result1; - // TODO Merge the decode and parsing of a multiple structure into one - int pos = 0; - for (int i = 0; i < count; i++) { - int num = counts[i]; - result[i] = new String[num]; - System.arraycopy(refs, pos, result[i], 0, num); - pos += num; - } - return result; - } - - /** - * Helper method to parse count references from in, - * using codec to decode the values as indexes into - * reference (which is populated prior to this call). An - * exception is thrown if a decoded index falls outside the range - * [0..reference.length-1]. - * - * @param name - * TODO - * @param in - * the input stream to read from - * @param codec - * the codec to use for decoding - * @param count - * the number of references to decode - * @param reference - * the array of values to use for the indexes; often - * {@link #cpUTF8} - * - * @throws IOException - * if a problem occurs during reading from the underlying stream - * @throws Pack200Exception - * if a problem occurs with an unexpected value or unsupported - * codec - */ - private String[] parseReferences(String name, InputStream in, - BHSDCodec codec, int count, String[] reference) throws IOException, - Pack200Exception { - return parseReferences(name, in, codec, 1, new int[] { count }, - reference)[0]; - } - - /** - * This performs the actual work of parsing against a non-static instance of - * Segment. - * - * @param in - * the input stream to read from - * @throws IOException - * if a problem occurs during reading from the underlying stream - * @throws Pack200Exception - * if a problem occurs with an unexpected value or unsupported - * codec - */ - private void parseSegment(InputStream in) throws IOException, - Pack200Exception { - debug("-------"); - parseSegmentHeader(in); - if (bandHeadersSize > 0) { - byte[] bandHeaders = new byte[(int) bandHeadersSize]; - readFully(in, bandHeaders); - setBandHeadersData(bandHeaders); - } - parseCpUtf8(in); - parseCpInt(in); - parseCpFloat(in); - parseCpLong(in); - parseCpDouble(in); - parseCpString(in); - parseCpClass(in); - parseCpSignature(in); - parseCpDescriptor(in); - parseCpField(in); - parseCpMethod(in); - parseCpIMethod(in); - parseAttributeDefinition(in); - parseIcBands(in); - parseClassBands(in); - parseBcBands(in); - parseFileBands(in); - } - - private void parseSegmentHeader(InputStream in) throws IOException, - Pack200Exception, Error, Pack200Exception { - long word[] = decodeScalar("archive_magic_word", in, Codec.BYTE1, - magic.length); - for (int m = 0; m < magic.length; m++) - if (word[m] != magic[m]) - throw new Error("Bad header"); - setArchiveMinorVersion((int) decodeScalar("archive_minver", in, - Codec.UNSIGNED5)); - setArchiveMajorVersion((int) decodeScalar("archive_majver", in, - Codec.UNSIGNED5)); - setOptions(new SegmentOptions((int) decodeScalar("archive_options", in, - Codec.UNSIGNED5))); - parseArchiveFileCounts(in); - parseArchiveSpecialCounts(in); - parseCpCounts(in); - parseClassCounts(in); - } - - private void processFileBits(InputStream in) throws IOException, - Pack200Exception { - // now read in the bytes - fileBits = new byte[numberOfFiles][]; - for (int i = 0; i < numberOfFiles; i++) { - int size = (int) fileSize[i]; - // TODO This buggers up if file_size > 2^32. Probably an array is - // not the right choice, and - // we should just serialize the bugger here? - fileBits[i] = new byte[size]; - for (int j = 0; j < size; j++) { - fileBits[i][j] = (byte) Codec.BYTE1.decode(in); - } - } - } - - /** - * Sets the major version of this archive. - * - * @param version - * the minor version of the archive - * @throws Pack200Exception - * if the major version is not 150 - */ - private void setArchiveMajorVersion(int version) throws Pack200Exception { - if (version != 150) - throw new Pack200Exception("Invalid segment major version"); - archiveMajor = version; - } - - /** - * Sets the minor version of this archive - * - * @param version - * the minor version of the archive - * @throws Pack200Exception - * if the minor version is not 7 - */ - private void setArchiveMinorVersion(int version) throws Pack200Exception { - if (version != 7) - throw new Pack200Exception("Invalid segment minor version"); - archiveMinor = version; - } - - public void setArchiveModtime(long archiveModtime) { - this.archiveModtime = archiveModtime; - } - - public void setArchiveSize(long archiveSize) { - this.archiveSize = archiveSize; - } - - private void setAttributeDefinitionCount(long valuie) { - this.attributeDefinitionCount = (int) valuie; - } - - private void setBandHeadersData(byte[] bandHeaders) { - this.bandHeadersInputStream = new ByteArrayInputStream(bandHeaders); - } - - private void setBandHeadersSize(long value) { - this.bandHeadersSize = (int) value; - } - - private void setClassCount(long value) { - classCount = (int) value; - } - - private void setCPClassCount(long value) { - cpClassCount = (int) value; - } - - private void setCPDescriptorCount(long value) { - cpDescriptorCount = (int) value; - } - - private void setCPDoubleCount(long value) { - cpDoubleCount = (int) value; - } - - private void setCPFieldCount(long value) { - cpFieldCount = (int) value; - } - - private void setCPFloatCount(long value) { - cpFloatCount = (int) value; - } - - private void setCPIMethodCount(long value) { - cpIMethodCount = (int) value; - } - - private void setCPIntCount(long value) { - cpIntCount = (int) value; - } - - private void setCPLongCount(long value) { - cpLongCount = (int) value; - } - - private void setCPMethodCount(long value) { - cpMethodCount = (int) value; - } - - private void setCPSignatureCount(long value) { - cpSignatureCount = (int) value; - } - - private void setCPStringCount(long value) { - cpStringCount = (int) value; - } - - private void setCPUtf8Count(long value) { - cpUTF8Count = (int) value; - } - - private void setDefaultClassMajorVersion(long value) { - defaultClassMajorVersion = (int) value; - } - - private void setDefaultClassMinorVersion(long value) { - defaultClassMinorVersion = (int) value; - } - - private void setInnerClassCount(long value) { - innerClassCount = (int) value; - } - - public void setNumberOfFiles(long value) { - numberOfFiles = (int) value; - } - - private void setOptions(SegmentOptions options) { - this.options = options; - } - - public void setSegmentsRemaining(long value) { - segmentsRemaining = (int) value; - } - - /** - * This is only here to provide a mechanism to turn off the warnings (and to - * prevent anyone from accidentally removing them from the file) - * - * @deprecated this will be deleted in the future, once I've started to use - * them - * - */ - int shutUpAboutTheStupidNotReadVariablesYetIHaventImplementedIt() { - return archiveMajor + archiveMinor + cpLong.hashCode() - + icName.hashCode() + icOuterClass.hashCode() - + icThisClass.hashCode(); - } - - /** - * Unpacks a packed stream (either .pack. or .pack.gz) into a corresponding - * JarOuputStream. - * - * @throws Pack200Exception - * if there is a problem unpacking - * @throws IOException - * if there is a problem with I/O during unpacking - */ - public void unpack(InputStream in, JarOutputStream out) throws IOException, - Pack200Exception { - if (!in.markSupported()) - in = new BufferedInputStream(in); - // TODO Can handle multiple concatenated streams, so should deal with - // that possibility - parse(in).unpack(in, out); - } - - /** - * 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 < numberOfFiles; i++) { - String name = fileName[i]; - long modtime = archiveModtime + fileModtime[i]; - boolean deflate = (fileOptions[i] & 1) == 1 - || options.shouldDeflate(); - boolean isClass = (fileOptions[i] & 2) == 2 || name == null - || name.equals(""); - if (isClass) { - // pull from headers - if (name == null || name.equals("")) - name = classThis[classNum] + ".class"; - } - JarEntry entry = new JarEntry(name); - if (deflate) - entry.setMethod(JarEntry.DEFLATED); - entry.setTime(modtime); - out.putNextEntry(entry); - - if (isClass) { - // write to dos - ClassFile classFile = buildClassFile(classNum); - classFile.write(dos); - dos.flush(); - classNum++; - } else { - long size = fileSize[i]; - entry.setSize(size); - // TODO pull from in - byte[] data = fileBits[i]; - out.write(data); - } - } - dos.flush(); - out.finish(); - out.flush(); - } } Index: src/main/java/org/apache/harmony/pack200/AttrDefinitionBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/AttrDefinitionBands.java +++ src/main/java/org/apache/harmony/pack200/AttrDefinitionBands.java @@ -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.pack200; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * + */ +public class AttrDefinitionBands extends BandSet { + + private int[] attributeDefinitionHeader; + + private String[] attributeDefinitionLayout; + + private String[] attributeDefinitionName; + + private AttributeLayoutMap attributeDefinitionMap; + + private String[] cpUTF8; + + public AttrDefinitionBands(Segment segment) { + super(segment); + this.cpUTF8 = segment.getCpBands().getCpUTF8(); + } + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.BandSet#pack(java.io.OutputStream) + */ + public void pack(OutputStream outputStream) { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.BandSet#unpack(java.io.InputStream) + */ + public void unpack(InputStream in) throws IOException, + Pack200Exception { + int attributeDefinitionCount = header.getAttributeDefinitionCount(); + attributeDefinitionHeader = decodeBandInt("attr_definition_headers", + in, Codec.BYTE1, attributeDefinitionCount); + attributeDefinitionName = parseReferences("attr_definition_name", in, + Codec.UNSIGNED5, attributeDefinitionCount, cpUTF8); + attributeDefinitionLayout = parseReferences("attr_definition_layout", + in, Codec.UNSIGNED5, attributeDefinitionCount, cpUTF8); + if (attributeDefinitionCount > 0) + throw new Error("No idea what the adc is for yet"); + attributeDefinitionMap = new AttributeLayoutMap(); + } + + public AttributeLayoutMap getAttributeDefinitionMap() { + return attributeDefinitionMap; + } + +} Index: src/main/java/org/apache/harmony/pack200/BandSet.java =================================================================== --- src/main/java/org/apache/harmony/pack200/BandSet.java +++ src/main/java/org/apache/harmony/pack200/BandSet.java @@ -0,0 +1,272 @@ +/* + * 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; +import java.io.OutputStream; + +public abstract class BandSet { + + public abstract void unpack(InputStream inputStream) throws IOException, Pack200Exception; + + public abstract void pack(OutputStream outputStream); + + protected Segment segment; + + protected SegmentHeader header; + + public BandSet(Segment segment) { + this.segment = segment; + this.header = segment.getSegmentHeader(); + } + + /** + * Decode a band and return an array of int[] values + * + * @param name + * the name of the band (primarily for logging/debugging + * purposes) + * @param in + * the InputStream to decode from + * @param defaultCodec + * the default codec for this band + * @param count + * the number of elements to read + * @return an array of decoded int[] values + * @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[] decodeBandInt(String name, InputStream in, + BHSDCodec defaultCodec, int count) throws IOException, + Pack200Exception { + // TODO Might be able to improve this directly. + int[] result = new int[count]; + + // TODO We need to muck around in the scenario where the first value + // read indicates + // an uber-codec + long[] longResult = decodeBandLong(name, in, defaultCodec, count); + for (int i = 0; i < count; i++) { + result[i] = (int) longResult[i]; + } + return result; + } + + /** + * Decode a band and return an array of long[] values + * + * @param name + * the name of the band (primarily for logging/debugging + * purposes) + * @param in + * the InputStream to decode from + * @param codec + * the default codec for this band + * @param count + * the number of elements to read + * @return an array of decoded long[] values + * @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 long[] decodeBandLong(String name, InputStream in, BHSDCodec codec, + int count) throws IOException, Pack200Exception { + if (codec.getB() == 1 || count == 0) { + return codec.decode(count, in); + } + long[] getFirst = codec.decode(1, in); + if (getFirst.length == 0) { + return getFirst; + } + long first = getFirst[0]; + if (codec.isSigned() && first >= -256 && first <= -1) { + // Non-default codec should be used + Codec nonDefaultCodec = CodecEncoding.getCodec((int) (-1 - first), + header.getBandHeadersInputStream(), codec); + return nonDefaultCodec.decode(count, in); + } else if (!codec.isSigned() && first >= codec.getL() + && first <= codec.getL() + 255) { + // Non-default codec should be used + Codec nonDefaultCodec = CodecEncoding.getCodec((int) first + - codec.getL(), header.getBandHeadersInputStream(), codec); + return nonDefaultCodec.decode(count, in); + } else { + // First element should not be discarded + return codec.decode(count - 1, in, first); + } + } + + public long[] parseFlags(String name, InputStream in, int count, + Codec codec, boolean hasHi) throws IOException, Pack200Exception { + return parseFlags(name, in, 1, new int[] { count }, (hasHi ? codec + : null), codec)[0]; + } + + public long[][] parseFlags(String name, InputStream in, int count, + int counts[], Codec codec, boolean hasHi) throws IOException, + Pack200Exception { + return parseFlags(name, in, count, counts, (hasHi ? codec : null), + codec); + } + + public long[][] parseFlags(String name, InputStream in, int count, + int counts[], Codec hiCodec, Codec loCodec) throws IOException, + Pack200Exception { + // TODO Move away from decoding into a parseBand type structure + if (count == 0) { + return new long[][] { {} }; + } + long[][] result = new long[count][]; + // TODO What happens when the decode here indicates a different + // encoding? + // TODO Move this to a decodeBandInt + long last = 0; + for (int j = 0; j < count; j++) { + result[j] = new long[counts[j]]; + for (int i = 0; i < counts[j] && hiCodec != null; i++) { + last = hiCodec.decode(in, last); + result[j][i] = last << 32; + } + } + last = 0; + for (int j = 0; j < count; j++) + for (int i = 0; i < counts[j]; i++) { + last = loCodec.decode(in, last); + result[j][i] = result[j][i] | last; + } + // TODO Remove debugging code + debug("Parsed *" + name + " (" + result.length + ")"); + return result; + } + + /** + * Helper method to parse count references from in, + * using codec to decode the values as indexes into + * reference (which is populated prior to this call). An + * exception is thrown if a decoded index falls outside the range + * [0..reference.length-1]. + * + * @param name + * TODO + * @param in + * the input stream to read from + * @param codec + * the codec to use for decoding + * @param count + * the number of references to decode + * @param reference + * the array of values to use for the indexes; often + * {@link #cpUTF8} + * + * @throws IOException + * if a problem occurs during reading from the underlying stream + * @throws Pack200Exception + * if a problem occurs with an unexpected value or unsupported + * codec + */ + public String[] parseReferences(String name, InputStream in, + BHSDCodec codec, int count, String[] reference) throws IOException, + Pack200Exception { + return parseReferences(name, in, codec, 1, new int[] { count }, + reference)[0]; + } + + /** + * Helper method to parse count references from in, + * using codec to decode the values as indexes into + * reference (which is populated prior to this call). An + * exception is thrown if a decoded index falls outside the range + * [0..reference.length-1]. Unlike the other parseReferences, this + * post-processes the result into an array of results. + * + * @param name + * TODO + * @param in + * the input stream to read from + * @param codec + * the codec to use for decoding + * @param count + * the number of references to decode + * @param reference + * the array of values to use for the indexes; often + * {@link #cpUTF8} + * + * @throws IOException + * if a problem occurs during reading from the underlying stream + * @throws Pack200Exception + * if a problem occurs with an unexpected value or unsupported + * codec + */ + protected String[][] parseReferences(String name, InputStream in, + BHSDCodec codec, int count, int counts[], String[] reference) + throws IOException, Pack200Exception { + if (count == 0) { + return new String[][] { {} }; + } + String[][] result = new String[count][]; + int sum = 0; + for (int i = 0; i < count; i++) { + result[i] = new String[counts[i]]; + sum += counts[i]; + } + // TODO Merge the decode and parsing of a multiple structure into one + String[] result1 = new String[sum]; + int[] decode = decodeBandInt(name, in, codec, sum); + for (int i1 = 0; i1 < sum; i1++) { + int index = decode[i1]; + if (index < 0 || index >= reference.length) + throw new Pack200Exception( + "Something has gone wrong during parsing references"); + result1[i1] = reference[index]; + } + String[] refs = result1; + // TODO Merge the decode and parsing of a multiple structure into one + int pos = 0; + for (int i = 0; i < count; i++) { + int num = counts[i]; + result[i] = new String[num]; + System.arraycopy(refs, pos, result[i], 0, num); + pos += num; + } + return result; + } + + /** + * 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 + * 'debug.pack200' is set, this will generate messages to stderr; otherwise, + * it will be silent. + * + * @param message + * @deprecated this should be removed from production code + */ + protected void debug(String message) { + if (System.getProperty("debug.pack200") != null) { + System.err.println(message); + } + } + + +} Index: src/main/java/org/apache/harmony/pack200/BcBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/BcBands.java +++ src/main/java/org/apache/harmony/pack200/BcBands.java @@ -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.pack200; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; + +import org.apache.harmony.pack200.bytecode.ByteCode; +import org.apache.harmony.pack200.bytecode.CodeAttribute; + +/** + * + */ +public class BcBands extends BandSet { + + private byte[][][] methodByteCodePacked; + + /** + * @param header + */ + public BcBands(Segment segment) { + super(segment); + } + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.BandSet#pack(java.io.OutputStream) + */ + public void pack(OutputStream outputStream) { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.BandSet#unpack(java.io.InputStream) + */ + public void unpack(InputStream in) throws IOException, + Pack200Exception { + + AttributeLayoutMap attributeDefinitionMap = segment.getAttrDefinitionBands().getAttributeDefinitionMap(); + int classCount = header.getClassCount(); + long[][] methodFlags = segment.getClassBands().getMethodFlags(); + int[] codeMaxNALocals = segment.getClassBands().getCodeMaxNALocals(); + int[] codeMaxStack = segment.getClassBands().getCodeMaxStack(); + ArrayList[][] methodAttributes = segment.getClassBands().getMethodAttributes(); + String[][] methodDescr = segment.getClassBands().getMethodDescr(); + + int bcStringRefCount = 0; + int bcInitRefCount = 0; + int bcFieldRefCount = 0; + int bcThisFieldCount = 0; + int bcMethodRefCount = 0; + int bcIMethodRefCount = 0; + + AttributeLayout abstractModifier = attributeDefinitionMap + .getAttributeLayout(AttributeLayout.ACC_ABSTRACT, + AttributeLayout.CONTEXT_METHOD); + AttributeLayout nativeModifier = attributeDefinitionMap + .getAttributeLayout(AttributeLayout.ACC_NATIVE, + AttributeLayout.CONTEXT_METHOD); + AttributeLayout staticModifier = attributeDefinitionMap + .getAttributeLayout(AttributeLayout.ACC_STATIC, + AttributeLayout.CONTEXT_METHOD); + methodByteCodePacked = new byte[classCount][][]; + int bcParsed = 0; + for (int c = 0; c < classCount; c++) { + int numberOfMethods = methodFlags[c].length; + methodByteCodePacked[c] = new byte[numberOfMethods][]; + for (int m = 0; m < numberOfMethods; m++) { + long methodFlag = methodFlags[c][m]; + if (!abstractModifier.matches(methodFlag) + && !nativeModifier.matches(methodFlag)) { + ByteArrayOutputStream codeBytes = new ByteArrayOutputStream(); + byte code; + while ((code = (byte) (0xff & in.read())) != -1) + codeBytes.write(code); + methodByteCodePacked[c][m] = codeBytes.toByteArray(); + bcParsed += methodByteCodePacked[c][m].length; + for (int i = 0; i < methodByteCodePacked[c][m].length; i++) { + int codePacked = 0xff & methodByteCodePacked[c][m][i]; + // TODO a lot of this needs to be encapsulated in the + // place that + // calculates what the arguments are, since (a) it will + // need + // to know where to get them, and (b) what to do with + // them + // once they've been gotten. But that's for another + // time. + switch (codePacked) { + case 18: // (a)ldc + bcStringRefCount++; + break; + case 178: // getstatic + case 179: // putstatic + case 180: // getfield + case 181: // putfield + bcFieldRefCount++; + break; + case 182: // invokevirtual + case 183: // invokespecial + case 184: // invokestatic + bcMethodRefCount++; + break; + case 185: // invokeinterface + bcIMethodRefCount++; + break; + case 202: // getstatic_this + case 203: // putstatic_this + case 204: // getfield_this + case 205: // putfield_this + bcThisFieldCount++; + break; + case 231: // invoke_special_init + bcInitRefCount++; + break; + + default: // unhandled specifically at this stage + debug("Found unhandled " + + ByteCode.getByteCode(codePacked)); + } + } + } + } + } + // other bytecode bands + debug("Parsed *bc_codes (" + bcParsed + ")"); + debug("unimplemented bc_case_count"); + debug("unimplemented bc_case_value"); + debug("unimplemented bc_byte"); + debug("unimplemented bc_short"); + debug("unimplemented bc_local"); + debug("unimplemented bc_label"); + debug("unimplemented bc_intref"); + debug("unimplemented bc_floatref"); + debug("unimplemented bc_longref"); + debug("unimplemented bc_doubleref"); + int[] bcStringRef = decodeBandInt("bc_stringref", in, Codec.DELTA5, + bcStringRefCount); + debug("unimplemented bc_classref"); + int[] bcFieldRef = decodeBandInt("bc_fieldref", in, Codec.DELTA5, + bcFieldRefCount); + int[] bcMethodRef = decodeBandInt("bc_methodref", in, Codec.UNSIGNED5, + bcMethodRefCount); + int[] bcIMethodRef = decodeBandInt("bc_imethodref", in, Codec.DELTA5, + bcIMethodRefCount); + int[] bcThisField = decodeBandInt("bc_thisfield", in, Codec.UNSIGNED5, + bcThisFieldCount); + debug("unimplemented bc_superfield"); + debug("unimplemented bc_thismethod"); + debug("unimplemented bc_supermethod"); + debug("unimplemented bc_initref"); + int[] bcInitRef = decodeBandInt("bc_initref", in, Codec.UNSIGNED5, + bcInitRefCount); + debug("unimplemented bc_escref"); + debug("unimplemented bc_escrefsize"); + debug("unimplemented bc_escsize"); + debug("unimplemented bc_escbyte"); + int i = 0; + for (int c = 0; c < classCount; c++) { + int numberOfMethods = methodFlags[c].length; + for (int m = 0; m < numberOfMethods; m++) { + long methodFlag = methodFlags[c][m]; + if (!abstractModifier.matches(methodFlag) + && !nativeModifier.matches(methodFlag)) { + int maxStack = codeMaxStack[i]; + int maxLocal = codeMaxNALocals[i]; + if (!staticModifier.matches(methodFlag)) + maxLocal++; // one for 'this' parameter + maxLocal += SegmentUtils.countArgs(methodDescr[c][m]); + // TODO Move creation of code attribute until after constant + // pool resolved + CodeAttribute attr = new CodeAttribute(maxStack, maxLocal, + methodByteCodePacked[c][m]); + methodAttributes[c][m].add(attr); + i++; + } + } + } + + } + +} Index: src/main/java/org/apache/harmony/pack200/ClassBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/ClassBands.java +++ src/main/java/org/apache/harmony/pack200/ClassBands.java @@ -0,0 +1,523 @@ +/* + * 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; +import java.io.OutputStream; +import java.util.ArrayList; + +import org.apache.harmony.pack200.bytecode.CPClass; +import org.apache.harmony.pack200.bytecode.ConstantValueAttribute; +import org.apache.harmony.pack200.bytecode.ExceptionsAttribute; + +/** + * + */ +public class ClassBands extends BandSet { + + private int classAttrCount; + + private int[] classFieldCount; + + private long[] classFlags; + + private String[][] classInterfaces; + + private int[] classMethodCount; + + private String[] classSuper; + + private String[] classThis; + + private int[] codeHandlerCount; + + private int[] codeMaxNALocals; + + private int[] codeMaxStack; + + private int fieldAttrCount; + + private ArrayList[][] fieldAttributes; + + private String[][] fieldDescr; + + private long[][] fieldFlags; + + private int methodAttrCount; + + private ArrayList[][] methodAttributes; + + private String[][] methodDescr; + + private ExceptionsAttribute[][] methodExceptions; + + private long[][] methodFlags; + + private AttributeLayoutMap attrMap; + + private CpBands cpBands; + + /** + * @param header + */ + public ClassBands(Segment segment){ + super(segment); + this.attrMap = segment.getAttrDefinitionBands().getAttributeDefinitionMap(); + this.cpBands = segment.getCpBands(); + } + + /* + * (non-Javadoc) + * + * @see org.apache.harmony.pack200.BandSet#pack(java.io.OutputStream) + */ + public void pack(OutputStream outputStream) { + // TODO Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see org.apache.harmony.pack200.BandSet#unpack(java.io.InputStream) + */ + public void unpack(InputStream in) throws IOException, + Pack200Exception { + int classCount = header.getClassCount(); + classThis = parseReferences("class_this", in, Codec.DELTA5, classCount, + cpBands.getCpClass()); + classSuper = parseReferences("class_super", in, Codec.DELTA5, + classCount, cpBands.getCpClass()); + classInterfaces = new String[classCount][]; + int[] classInterfaceLengths = decodeBandInt("class_interface_count", + in, Codec.DELTA5, classCount); + // for (int i = 0; i < classCount; i++) { + classInterfaces = parseReferences("class_interface", in, Codec.DELTA5, + classCount, classInterfaceLengths, cpBands.getCpClass()); + // } + classFieldCount = decodeBandInt("class_field_count", in, Codec.DELTA5, + classCount); + classMethodCount = decodeBandInt("class_method_count", in, + Codec.DELTA5, classCount); + parseFieldBands(in); + parseMethodBands(in); + parseClassAttrBands(in); + parseCodeBands(in); + + } + + private void parseFieldBands(InputStream in) throws IOException, + Pack200Exception { + int classCount = header.getClassCount(); + SegmentOptions options = header.getOptions(); + fieldDescr = parseReferences("field_descr", in, Codec.DELTA5, + classCount, classFieldCount, cpBands.getCpDescriptor()); + fieldFlags = parseFlags("field_flags", in, classCount, classFieldCount, + Codec.UNSIGNED5, options.hasFieldFlagsHi()); + for (int i = 0; i < classCount; i++) { + for (int j = 0; j < fieldFlags[i].length; j++) { + long flag = fieldFlags[i][j]; + if ((flag & (1 << 16)) != 0) + fieldAttrCount++; + } + } + if (fieldAttrCount > 0) + throw new Error( + "There are attribute flags, and I don't know what to do with them"); + debug("unimplemented field_attr_indexes"); + debug("unimplemented field_attr_calls"); + AttributeLayout layout = attrMap.getAttributeLayout( + "ConstantValue", AttributeLayout.CONTEXT_FIELD); + Codec codec = layout.getCodec(); + fieldAttributes = new ArrayList[classCount][]; + for (int i = 0; i < classCount; i++) { + fieldAttributes[i] = new ArrayList[fieldFlags[i].length]; + for (int j = 0; j < fieldFlags[i].length; j++) { + fieldAttributes[i][j] = new ArrayList(); + long flag = fieldFlags[i][j]; + if (layout.matches(flag)) { + // we've got a value to read + long result = codec.decode(in); + String desc = fieldDescr[i][j]; + int colon = desc.indexOf(':'); + // String name = desc.substring(0, colon); + String type = desc.substring(colon + 1); + // TODO Got to get better at this ... in any case, it should + // be e.g. KIB or KIH + if (type.equals("B") || type.equals("H")) + type = "I"; + Object value = layout.getValue(result, type, cpBands.getConstantPool()); + fieldAttributes[i][j] + .add(new ConstantValueAttribute(value)); + debug("Processed value " + value + " for ConstantValue"); + } + } + } + debug("unimplemented field_Signature_RS"); + parseMetadataBands(AttributeLayout.CONTEXT_FIELD); + } + + private void parseMethodBands(InputStream in) throws IOException, + Pack200Exception { + int classCount = header.getClassCount(); + SegmentOptions options = header.getOptions(); + methodDescr = parseReferences("method_descr", in, Codec.MDELTA5, + classCount, classMethodCount, cpBands.getCpDescriptor()); + methodFlags = parseFlags("method_flags", in, classCount, + classMethodCount, 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"); + // assign empty method attributes + methodAttributes = new ArrayList[classCount][]; + for (int i = 0; i < classCount; i++) { + methodAttributes[i] = new ArrayList[methodFlags[i].length]; + for (int j = 0; j < methodFlags[i].length; j++) { + methodAttributes[i][j] = new ArrayList(); + } + } + parseAttributeMethodExceptions(in); + parseAttributeMethodSignature(in); + parseMetadataBands(AttributeLayout.CONTEXT_METHOD); + } + + /** + * @param in + * @throws Pack200Exception + * @throws IOException + */ + private void parseAttributeMethodExceptions(InputStream in) + throws Pack200Exception, IOException { + // TODO Should refactor this stuff into the layout somehow + int classCount = header.getClassCount(); + AttributeLayout layout = attrMap.getAttributeLayout( + "Exceptions", AttributeLayout.CONTEXT_METHOD); + Codec codec = layout.getCodec(); + methodExceptions = new ExceptionsAttribute[classCount][]; + int[][] numExceptions = new int[classCount][]; + for (int i = 0; i < classCount; i++) { + numExceptions[i] = new int[methodFlags[i].length]; + for (int j = 0; j < methodFlags[i].length; j++) { + long flag = methodFlags[i][j]; + if (layout.matches(flag)) { + numExceptions[i][j] = (int) codec.decode(in); + } + } + } + for (int i = 0; i < classCount; i++) { + methodExceptions[i] = new ExceptionsAttribute[methodFlags[i].length]; + for (int j = 0; j < methodFlags[i].length; j++) { + long flag = methodFlags[i][j]; + int n = numExceptions[i][j]; + if (n > 0) { + CPClass[] exceptions = new CPClass[n]; + if (layout.matches(flag)) { + for (int k = 0; k < n; k++) { + long result = codec.decode(in); + exceptions[k] = new CPClass(cpBands.getCpClass()[(int) result]); + } + } + methodExceptions[i][j] = new ExceptionsAttribute(exceptions); + methodAttributes[i][j].add(methodExceptions[i][j]); + } + } + } + } + + + /** + * @param name + * @param flags + * @throws Pack200Exception + */ + private void parseAttributeUnknown(String name, int context, long[][] flags) + throws Pack200Exception { + debug("Parsing unknown attributes for " + name); + AttributeLayout layout = attrMap.getAttributeLayout( + name, context); + int count = SegmentUtils.countMatches(flags, layout); + if (count > 0) + throw new Error("We've got data for " + name + + " and we don't know what to do with it (yet)"); + } + + /** + * @param in + * + */ + private void parseAttributeMethodSignature(InputStream in) + throws Pack200Exception, IOException { + parseAttributeUnknown(AttributeLayout.ATTRIBUTE_SIGNATURE, + AttributeLayout.CONTEXT_METHOD, methodFlags); + } + + private void parseClassAttrBands(InputStream in) throws IOException, + Pack200Exception { + int classCount = header.getClassCount(); + SegmentOptions options = header.getOptions(); + classFlags = parseFlags("class_flags", in, classCount, Codec.UNSIGNED5, + options.hasClassFlagsHi()); + for (int i = 0; i < classCount; i++) { + long flag = classFlags[i]; + if ((flag & (1 << 16)) != 0) + classAttrCount++; + } + if (classAttrCount > 0) + 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 = attrMap.getAttributeLayout( + AttributeLayout.ATTRIBUTE_SOURCE_FILE, + 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 + // TODO File this as a sourcefile attribute and don't generate + // everything below + long result = layout.getCodec().decode(in); + Object value = layout.getValue(result, cpBands.getConstantPool()); + debug("Processed value " + value + " for SourceFile"); + } + } + debug("unimplemented class_EnclosingMethod_RC"); + debug("unimplemented class_EnclosingMethod_RDN"); + debug("unimplemented class_Signature_RS"); + parseMetadataBands(AttributeLayout.CONTEXT_CLASS); + debug("unimplemented class_InnerClasses_N"); + debug("unimplemented class_InnerClasses_RC"); + debug("unimplemented class_InnerClasses_F"); + debug("unimplemented class_InnerClasses_outer_RCN"); + debug("unimplemented class_InnerClasses_inner_RCN"); + debug("unimplemented class_file_version_minor_H"); + debug("unimplemented class_file_version_major_H"); + } + + + private void parseCodeBands(InputStream in) throws Pack200Exception, + IOException { + AttributeLayout layout = attrMap.getAttributeLayout( + AttributeLayout.ATTRIBUTE_CODE, AttributeLayout.CONTEXT_METHOD); + + int codeBands = SegmentUtils.countMatches(methodFlags, layout); + int[] codeHeaders = decodeBandInt("code_headers", in, Codec.BYTE1, + codeBands); + int codeSpecialHeader = 0; + for (int i = 0; i < codeBands; i++) { + if (codeHeaders[i] == 0) + codeSpecialHeader++; + } + int[] codeMaxStackSpecials = decodeBandInt("code_max_stack", in, + Codec.UNSIGNED5, codeSpecialHeader); + int[] codeMaxNALocalsSpecials = decodeBandInt("code_max_na_locals", in, + Codec.UNSIGNED5, codeSpecialHeader); + int[] codeHandlerCountSpecials = decodeBandInt("code_handler_count", + in, Codec.UNSIGNED5, codeSpecialHeader); + + codeMaxStack = new int[codeBands]; + codeMaxNALocals = new int[codeBands]; + codeHandlerCount = new int[codeBands]; + int special = 0; + for (int i = 0; i < codeBands; i++) { + int header = 0xff & codeHeaders[i]; + if (header < 0) { + throw new IllegalStateException("Shouldn't get here"); + } else if (header == 0) { + codeMaxStack[i] = codeMaxStackSpecials[special]; + codeMaxNALocals[i] = codeMaxNALocalsSpecials[special]; + codeHandlerCount[i] = codeHandlerCountSpecials[special]; + special++; + } else if (header <= 144) { + codeMaxStack[i] = (header - 1) % 12; + codeMaxNALocals[i] = (header - 1) / 12; + codeHandlerCount[i] = 0; + } else if (header <= 208) { + codeMaxStack[i] = (header - 145) % 8; + codeMaxNALocals[i] = (header - 145) / 8; + codeHandlerCount[i] = 1; + } else if (header <= 255) { + codeMaxStack[i] = (header - 209) % 7; + codeMaxNALocals[i] = (header - 209) / 7; + codeHandlerCount[i] = 2; + } else { + throw new IllegalStateException("Shouldn't get here either"); + } + } + 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"); + debug("unimplemented code_attr_indexes"); + debug("unimplemented code_attr_calls"); + debug("unimplemented code_LineNumberTable_N"); + debug("unimplemented code_LineNumberTable_bci_P"); + debug("unimplemented code_LineNumberTable_line"); + String[] types = { "LocalVariableTable", "LocalVariableTypeTable" }; + for (int i = 0; i < types.length; i++) { + String type = types[i]; + debug("unimplemented code_" + type + "_N"); + debug("unimplemented code_" + type + "_bci_P"); + debug("unimplemented code_" + type + "_span_O"); + debug("unimplemented code_" + type + "_name_RU"); + debug("unimplemented code_" + type + "_type_RS"); + debug("unimplemented code_" + type + "_slot"); + } + } + + private void parseMetadataBands(int context) throws Pack200Exception { + String[] RxA; + if (AttributeLayout.CONTEXT_METHOD == context) { + RxA = new String[] { "RVA", "RIA", "RVPA", "RIPA", "AD" }; + } else if (AttributeLayout.CONTEXT_FIELD == context + || AttributeLayout.CONTEXT_CLASS == context) { + RxA = new String[] { "RVA", "RIA" }; + } else { + throw new Pack200Exception("Unknown type of metadata unit " + + context); + } + // AttributeLayout layout = + // map.get(RuntimeVisibleAnnotations,class/field/method as int) + // foreachheader ... + // if layout.matches(header[n] or whatever) + String contextName = (AttributeLayout.CONTEXT_METHOD == context ? "method" + : (AttributeLayout.CONTEXT_FIELD == context ? "field" + : (AttributeLayout.CONTEXT_CLASS == context ? "class" + : "unkowon"))); + for (int i = 0; i < RxA.length; i++) { + String rxa = RxA[i]; + if (rxa.indexOf("P") >= 0) { + debug("unimplemented " + contextName + "_" + rxa + "_param_NB"); + } + if (!rxa.equals("AD")) { + debug("unimplemented " + contextName + "_" + rxa + "_anno_N"); + debug("unimplemented " + contextName + "_" + rxa + "_type_RS"); + debug("unimplemented " + contextName + "_" + rxa + "_pair_N"); + debug("unimplemented " + contextName + "_" + rxa + "_name_RU"); + } + debug("unimplemented " + contextName + "_" + rxa + "_T"); + debug("unimplemented " + contextName + "_" + rxa + "_caseI_KI"); + debug("unimplemented " + contextName + "_" + rxa + "_caseD_KD"); + debug("unimplemented " + contextName + "_" + rxa + "_caseF_KF"); + debug("unimplemented " + contextName + "_" + rxa + "_caseJ_KJ"); + debug("unimplemented " + contextName + "_" + rxa + "_casec_RS"); + debug("unimplemented " + contextName + "_" + rxa + "_caseet_RS"); + debug("unimplemented " + contextName + "_" + rxa + "_caseec_RU"); + debug("unimplemented " + contextName + "_" + rxa + "_cases_RU"); + debug("unimplemented " + contextName + "_" + rxa + "_casearray_N"); + debug("unimplemented " + contextName + "_" + rxa + "_nesttype_RS"); + debug("unimplemented " + contextName + "_" + rxa + "_nestpair_N"); + debug("unimplemented " + contextName + "_" + rxa + "_nestname_RU"); + } + } + + public int getClassAttrCount() { + return classAttrCount; + } + + public int[] getClassFieldCount() { + return classFieldCount; + } + + public long[] getClassFlags() { + return classFlags; + } + + public String[][] getClassInterfaces() { + return classInterfaces; + } + + public int[] getClassMethodCount() { + return classMethodCount; + } + + public String[] getClassSuper() { + return classSuper; + } + + public String[] getClassThis() { + return classThis; + } + + public int[] getCodeMaxNALocals() { + return codeMaxNALocals; + } + + public int[] getCodeMaxStack() { + return codeMaxStack; + } + + public int getFieldAttrCount() { + return fieldAttrCount; + } + + public ArrayList[][] getFieldAttributes() { + return fieldAttributes; + } + + public String[][] getFieldDescr() { + return fieldDescr; + } + + public long[][] getFieldFlags() { + return fieldFlags; + } + + public int getMethodAttrCount() { + return methodAttrCount; + } + + public ArrayList[][] getMethodAttributes() { + return methodAttributes; + } + + public String[][] getMethodDescr() { + return methodDescr; + } + + public ExceptionsAttribute[][] getMethodExceptions() { + return methodExceptions; + } + + public long[][] getMethodFlags() { + return methodFlags; + } + +} Index: src/main/java/org/apache/harmony/pack200/CpBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/CpBands.java +++ src/main/java/org/apache/harmony/pack200/CpBands.java @@ -0,0 +1,439 @@ +/* + * 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; +import java.io.OutputStream; +import java.util.ArrayList; + +public class CpBands extends BandSet { + + public SegmentConstantPool getConstantPool() { + return pool; + } + + + private final SegmentConstantPool pool = new SegmentConstantPool(this); + + private String[] cpClass; + + private String[] cpDescriptor; + + private double[] cpDouble; + + private String[] cpFieldClass; + + private Object cpFieldDescriptor; + + private float[] cpFloat; + + private String[] cpIMethodClass; + + private String[] cpIMethodDescriptor; + + private int[] cpInt; + + private long[] cpLong; + + private String[] cpMethodClass; + + private String[] cpMethodDescriptor; + + private String[] cpSignature; + + private String[] cpString; + + private String[] cpUTF8; + + + public CpBands(Segment segment) { + super(segment); + } + + public void unpack(InputStream in) throws IOException, Pack200Exception { + parseCpUtf8(in); + parseCpInt(in); + parseCpFloat(in); + parseCpLong(in); + parseCpDouble(in); + parseCpString(in); + parseCpClass(in); + parseCpSignature(in); + parseCpDescriptor(in); + parseCpField(in); + parseCpMethod(in); + parseCpIMethod(in); + } + + public void pack(OutputStream outputStream) { + + } + + /** + * Parses the constant pool class names, using {@link #cpClassCount} to + * populate {@link #cpClass} from {@link #cpUTF8}. + * + * @param in + * the input stream to read from + * @throws IOException + * if a problem occurs during reading from the underlying stream + * @throws Pack200Exception + * if a problem occurs with an unexpected value or unsupported + * codec + */ + private void parseCpClass(InputStream in) throws IOException, + Pack200Exception { + int cpClassCount = header.getCpClassCount(); + cpClass = parseReferences("cp_Class", in, Codec.UDELTA5, cpClassCount, + cpUTF8); + } + + + /** + * Parses the constant pool descriptor definitions, using + * {@link #cpDescriptorCount} to populate {@link #cpDescriptor}. For ease + * of use, the cpDescriptor is stored as a string of the form name:type, + * largely to make it easier for representing field and method descriptors + * (e.g. out:java.lang.PrintStream) in a way that is + * compatible with passing String arrays. + * + * @param in + * the input stream to read from + * @throws IOException + * if a problem occurs during reading from the underlying stream + * @throws Pack200Exception + * if a problem occurs with an unexpected value or unsupported + * codec + */ + private void parseCpDescriptor(InputStream in) throws IOException, + Pack200Exception { + int cpDescriptorCount = header.getCpDescriptorCount(); + String[] cpDescriptorNames = parseReferences("cp_Descr_name", in, + Codec.DELTA5, cpDescriptorCount, cpUTF8); + String[] cpDescriptorTypes = parseReferences("cp_Descr_type", in, + Codec.UDELTA5, cpDescriptorCount, cpSignature); + cpDescriptor = new String[cpDescriptorCount]; + for (int i = 0; i < cpDescriptorCount; i++) { + cpDescriptor[i] = cpDescriptorNames[i] + ":" + cpDescriptorTypes[i]; //$NON-NLS-1$ + } + } + + private void parseCpDouble(InputStream in) throws IOException, + Pack200Exception { + int cpDoubleCount = header.getCpDoubleCount(); + cpDouble = new double[cpDoubleCount]; + long[] hiBits = decodeBandLong("cp_Double_hi", in, Codec.UDELTA5, + cpDoubleCount); + long[] loBits = decodeBandLong("cp_Double_lo", in, Codec.DELTA5, + cpDoubleCount); + for (int i = 0; i < cpDoubleCount; i++) { + cpDouble[i] = Double.longBitsToDouble(hiBits[i] << 32 | loBits[i]); + } + } + + /** + * Parses the constant pool field definitions, using {@link #cpFieldCount} + * to populate {@link #cpFieldClass} and {@link #cpFieldDescriptor}. + * + * @param in + * the input stream to read from + * @throws IOException + * if a problem occurs during reading from the underlying stream + * @throws Pack200Exception + * if a problem occurs with an unexpected value or unsupported + * codec + */ + private void parseCpField(InputStream in) throws IOException, + Pack200Exception { + int cpFieldCount = header.getCpFieldCount(); + cpFieldClass = parseReferences("cp_Field_class", in, Codec.DELTA5, + cpFieldCount, cpClass); + cpFieldDescriptor = parseReferences("cp_Field_desc", in, Codec.UDELTA5, + cpFieldCount, cpDescriptor); + } + + private void parseCpFloat(InputStream in) throws IOException, + Pack200Exception { + int cpFloatCount = header.getCpFloatCount(); + cpFloat = new float[cpFloatCount]; + int floatBits[] = decodeBandInt("cp_Float", in, Codec.UDELTA5, + cpFloatCount); + for (int i = 0; i < cpFloatCount; i++) { + cpFloat[i] = Float.intBitsToFloat(floatBits[i]); + } + } + + /** + * Parses the constant pool interface method definitions, using + * {@link #cpIMethodCount} to populate {@link #cpIMethodClass} and + * {@link #cpIMethodDescriptor}. + * + * @param in + * the input stream to read from + * @throws IOException + * if a problem occurs during reading from the underlying stream + * @throws Pack200Exception + * if a problem occurs with an unexpected value or unsupported + * codec + */ + private void parseCpIMethod(InputStream in) throws IOException, + Pack200Exception { + int cpIMethodCount = header.getCpIMethodCount(); + cpIMethodClass = parseReferences("cp_Imethod_class", in, Codec.DELTA5, + cpIMethodCount, cpClass); + cpIMethodDescriptor = parseReferences("cp_Imethod_desc", in, + Codec.UDELTA5, cpIMethodCount, cpDescriptor); + } + + private void parseCpInt(InputStream in) throws IOException, + Pack200Exception { + int cpIntCount = header.getCpIntCount(); + cpInt = new int[cpIntCount]; + long last = 0; + for (int i = 0; i < cpIntCount; i++) { + last = Codec.UDELTA5.decode(in, last); + cpInt[i] = (int) last; + } + } + + private void parseCpLong(InputStream in) throws IOException, + Pack200Exception { + int cpLongCount = header.getCpLongCount(); + cpLong = parseFlags("cp_Long", in, cpLongCount, new int[] { 1 }, + Codec.UDELTA5, Codec.DELTA5)[0]; + } + + /** + * Parses the constant pool method definitions, using {@link #cpMethodCount} + * to populate {@link #cpMethodClass} and {@link #cpMethodDescriptor}. + * + * @param in + * the input stream to read from + * @throws IOException + * if a problem occurs during reading from the underlying stream + * @throws Pack200Exception + * if a problem occurs with an unexpected value or unsupported + * codec + */ + private void parseCpMethod(InputStream in) throws IOException, + Pack200Exception { + int cpMethodCount = header.getCpMethodCount(); + cpMethodClass = parseReferences("cp_Method_class", in, Codec.DELTA5, + cpMethodCount, cpClass); + cpMethodDescriptor = parseReferences("cp_Method_desc", in, + Codec.UDELTA5, cpMethodCount, cpDescriptor); + } + + /** + * Parses the constant pool signature classes, using + * {@link #cpSignatureCount} to populate {@link #cpSignature}. A signature + * form is akin to the bytecode representation of a class; Z for boolean, I + * for int, [ for array etc. However, although classes are started with L, + * the classname does not follow the form; instead, there is a separate + * array of classes. So an array corresponding to + * public static void main(String args[]) has a form of + * [L(V) and a classes array of + * [java.lang.String]. The {@link #cpSignature} is a string + * representation identical to the bytecode equivalent + * [Ljava/lang/String;(V) TODO Check that the form is as + * above and update other types e.g. J + * + * @param in + * the input stream to read from + * @throws IOException + * if a problem occurs during reading from the underlying stream + * @throws Pack200Exception + * if a problem occurs with an unexpected value or unsupported + * codec + */ + private void parseCpSignature(InputStream in) throws IOException, + Pack200Exception { + int cpSignatureCount = header.getCpSignatureCount(); + String[] cpSignatureForm = parseReferences("cp_Signature_form", in, + Codec.DELTA5, cpSignatureCount, cpUTF8); + cpSignature = new String[cpSignatureCount]; + long last = 0; + for (int i = 0; i < cpSignatureCount; i++) { + String form = cpSignatureForm[i]; + int len = form.length(); + StringBuffer signature = new StringBuffer(64); + ArrayList list = new ArrayList(); + for (int j = 0; j < len; j++) { + char c = form.charAt(j); + signature.append(c); + if (c == 'L') { + int index = (int) (last = Codec.UDELTA5.decode(in, last)); + String className = cpClass[index]; + list.add(className); + signature.append(className); + } + } + cpSignature[i] = signature.toString(); + } + } + + /** + * Parses the constant pool strings, using {@link #cpStringCount} to + * populate {@link #cpString} from indexes into {@link #cpUTF8}. + * + * @param in + * the input stream to read from + * @throws IOException + * if a problem occurs during reading from the underlying stream + * @throws Pack200Exception + * if a problem occurs with an unexpected value or unsupported + * codec + */ + private void parseCpString(InputStream in) throws IOException, + Pack200Exception { + int cpStringCount = header.getCpStringCount(); + cpString = new String[cpStringCount]; + long last = 0; + for (int i = 0; i < cpStringCount; i++) { + int index = (int) (last = Codec.UDELTA5.decode(in, last)); + cpString[i] = cpUTF8[index]; + } + } + + private void parseCpUtf8(InputStream in) throws IOException, + Pack200Exception { + int cpUTF8Count = header.getCpUTF8Count(); + // TODO Update codec.decode -> decodeScalar + cpUTF8 = new String[cpUTF8Count]; + cpUTF8[0] = ""; //$NON-NLS-1$ + int[] prefix = new int[cpUTF8Count]; + int[] suffix = new int[cpUTF8Count]; + if (cpUTF8Count > 0) { + prefix[0] = 0; + suffix[0] = 0; + if (cpUTF8Count > 1) + prefix[1] = 0; + } + long last = 0; + for (int i = 2; i < cpUTF8Count; i++) { + last = prefix[i] = (int) Codec.DELTA5.decode(in, last); + } + int chars = 0; + int bigSuffix = 0; + for (int i = 1; i < cpUTF8Count; i++) { + last = suffix[i] = (int) Codec.UNSIGNED5.decode(in); + if (last == 0) { + bigSuffix++; + } else { + chars += last; + } + } + char data[] = new char[chars]; + for (int i = 0; i < data.length; i++) { + data[i] = (char) Codec.CHAR3.decode(in); + } + // read in the big suffix data + char bigSuffixData[][] = new char[bigSuffix][]; + last = 0; + for (int i = 0; i < bigSuffix; i++) { + last = (int) Codec.DELTA5.decode(in, last); + bigSuffixData[i] = new char[(int) last]; + } + // initialize big suffix data + for (int i = 0; i < bigSuffix; i++) { + char[] singleBigSuffixData = bigSuffixData[i]; + last = 0; + for (int j = 0; j < singleBigSuffixData.length; j++) { + last = singleBigSuffixData[j] = (char) Codec.DELTA5.decode(in, + last); + } + } + // go through the strings + chars = 0; + bigSuffix = 0; + for (int i = 1; i < cpUTF8Count; i++) { + String lastString = cpUTF8[i - 1]; + if (suffix[i] == 0) { + // The big suffix stuff hasn't been tested, and I'll be + // surprised if it works first time w/o errors ... + cpUTF8[i] = lastString.substring(0, prefix[i]) + + new String(bigSuffixData[bigSuffix++]); + } else { + cpUTF8[i] = lastString.substring(0, prefix[i]) + + new String(data, chars, suffix[i]); + chars += suffix[i]; + } + } + } + + public String[] getCpClass() { + return cpClass; + } + + public String[] getCpDescriptor() { + return cpDescriptor; + } + + public double[] getCpDouble() { + return cpDouble; + } + + public String[] getCpFieldClass() { + return cpFieldClass; + } + + public Object getCpFieldDescriptor() { + return cpFieldDescriptor; + } + + public float[] getCpFloat() { + return cpFloat; + } + + public String[] getCpIMethodClass() { + return cpIMethodClass; + } + + public String[] getCpIMethodDescriptor() { + return cpIMethodDescriptor; + } + + public int[] getCpInt() { + return cpInt; + } + + public long[] getCpLong() { + return cpLong; + } + + public String[] getCpMethodClass() { + return cpMethodClass; + } + + public String[] getCpMethodDescriptor() { + return cpMethodDescriptor; + } + + public String[] getCpSignature() { + return cpSignature; + } + + public String[] getCpString() { + return cpString; + } + + public String[] getCpUTF8() { + return cpUTF8; + } + +} Index: src/main/java/org/apache/harmony/pack200/FileBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/FileBands.java +++ src/main/java/org/apache/harmony/pack200/FileBands.java @@ -0,0 +1,147 @@ +/* + * 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; +import java.io.OutputStream; +import java.util.Arrays; + +/** + * Parses the file band headers (not including the actual bits themselves). + * At the end of this parse call, the input stream will be positioned at the + * start of the file_bits themselves, and there will be Sum(file_size) bits + * remaining in the stream with BYTE1 compression. A decent implementation + * will probably just stream the bytes out to the reconstituted Jar rather + * than caching them. + * + */ +public class FileBands extends BandSet { + + private byte[][] fileBits; + + private long[] fileModtime; + + private String[] fileName; + + private long[] fileOptions; + + private long[] fileSize; + + private String[] cpUTF8; + + /** + * @param header + */ + public FileBands(Segment segment) { + super(segment); + this.cpUTF8 = segment.getCpBands().getCpUTF8(); + } + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.BandSet#pack(java.io.OutputStream) + */ + public void pack(OutputStream outputStream) { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.BandSet#unpack(java.io.InputStream) + */ + public void unpack(InputStream in) throws IOException, + Pack200Exception { + int numberOfFiles = header.getNumberOfFiles(); + SegmentOptions options = header.getOptions(); + if (false && System.getProperty("debug.pack200") != null) { + // TODO HACK + fileSize = new long[numberOfFiles]; + fileModtime = new long[numberOfFiles]; + fileOptions = new long[numberOfFiles]; + fileName = new String[numberOfFiles]; + Arrays.fill(fileName, ""); + return; + } + long last; + fileName = parseReferences("file_name", in, Codec.UNSIGNED5, + numberOfFiles, cpUTF8); + fileSize = new long[numberOfFiles]; + if (options.hasFileSizeHi()) { + last = 0; + for (int i = 0; i < numberOfFiles; i++) { + fileSize[i] = (last = Codec.UNSIGNED5.decode(in, last)) << 32; + } + } + last = 0; + for (int i = 0; i < numberOfFiles; i++) { + fileSize[i] |= (last = Codec.UNSIGNED5.decode(in, last)); + } + fileModtime = new long[numberOfFiles]; + if (options.hasFileModtime()) { + last = 0; + for (int i = 0; i < numberOfFiles; i++) { + fileModtime[i] |= (last = Codec.DELTA5.decode(in, last)); + } + } + fileOptions = new long[numberOfFiles]; + if (options.hasFileOptions()) { + last = 0; + for (int i = 0; i < numberOfFiles; i++) { + fileOptions[i] |= (last = Codec.UNSIGNED5.decode(in, last)); + } + } + } + + + public void processFileBits(InputStream in) throws IOException, + Pack200Exception { + // now read in the bytes + int numberOfFiles = header.getNumberOfFiles(); + fileBits = new byte[numberOfFiles][]; + for (int i = 0; i < numberOfFiles; i++) { + int size = (int) fileSize[i]; + // TODO This buggers up if file_size > 2^32. Probably an array is + // not the right choice, and + // we should just serialize the bugger here? + fileBits[i] = new byte[size]; + for (int j = 0; j < size; j++) { + fileBits[i][j] = (byte) Codec.BYTE1.decode(in); + } + } + } + + public byte[][] getFileBits() { + return fileBits; + } + + public long[] getFileModtime() { + return fileModtime; + } + + public String[] getFileName() { + return fileName; + } + + public long[] getFileOptions() { + return fileOptions; + } + + public long[] getFileSize() { + return fileSize; + } + +} Index: src/main/java/org/apache/harmony/pack200/IcBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/IcBands.java +++ src/main/java/org/apache/harmony/pack200/IcBands.java @@ -0,0 +1,80 @@ +/* + * 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; +import java.io.OutputStream; + +/** + * + */ +public class IcBands extends BandSet { + + private int[] icFlags; + + private Object icName; + + private String[] icOuterClass; + + private String[] icThisClass; + + private String[] cpUTF8; + + private String[] cpClass; + + /** + * @param header + */ + public IcBands(Segment segment) { + super(segment); + this.cpClass = segment.getCpBands().getCpClass(); + this.cpUTF8 = segment.getCpBands().getCpUTF8(); + } + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.BandSet#pack(java.io.OutputStream) + */ + public void pack(OutputStream outputStream) { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.BandSet#unpack(java.io.InputStream) + */ + public void unpack(InputStream in) throws IOException, + Pack200Exception { + int innerClassCount = header.getInnerClassCount(); + icThisClass = parseReferences("ic_this_class", in, Codec.UDELTA5, + innerClassCount, cpClass); + icFlags = new int[innerClassCount]; + long last = 0; + int outerClasses = 0; + // ic_flags + for (int i = 0; i < innerClassCount; i++) { + icFlags[i] = (int) (last = Codec.UNSIGNED5.decode(in, last)); + if ((icFlags[i] & 1 << 16) != 0) + outerClasses++; + } + icOuterClass = parseReferences("ic_outer_class", in, Codec.DELTA5, + outerClasses, cpClass); + icName = parseReferences("ic_name", in, Codec.DELTA5, outerClasses, + cpUTF8); + } + +} Index: src/main/java/org/apache/harmony/pack200/SegmentConstantPool.java =================================================================== --- src/main/java/org/apache/harmony/pack200/SegmentConstantPool.java +++ src/main/java/org/apache/harmony/pack200/SegmentConstantPool.java @@ -0,0 +1,75 @@ +/* + * 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; + +public class SegmentConstantPool { + /** + * + */ + private CpBands bands; + + /** + * @param bands + */ + public SegmentConstantPool(CpBands bands) { + this.bands = bands; + } + + public static final int ALL = 0; + + public static final int CP_DOUBLE = 7; + + // define in archive order + + public static final int CP_FLOAT = 4; // TODO Check this + + public static final int CP_INT = 3; + + public static final int CP_LONG = 6; + + public static final int CP_STRING = 5; + + public static final int SIGNATURE = 2; // TODO and more to come -- + + 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; + } else if (index < 0) { + throw new Pack200Exception("Cannot have a negative range"); + } else if (cp == UTF_8) { + return bands.getCpUTF8()[index]; + } else if (cp == CP_STRING) { + return bands.getCpString()[index]; + } else if (cp == SIGNATURE) { + return bands.getCpSignature()[index]; + } else if (cp == CP_INT) { + return new Integer(bands.getCpInt()[index]); + } else if (cp == CP_FLOAT) { + return new Float(bands.getCpFloat()[index]); + } else if (cp == CP_DOUBLE) { + return new Double(bands.getCpDouble()[index]); + } else if (cp == CP_LONG) { + return new Long(bands.getCpLong()[index]); + } else { + // etc + throw new Error("Get value incomplete"); + } + } +} \ No newline at end of file Index: src/main/java/org/apache/harmony/pack200/SegmentHeader.java =================================================================== --- src/main/java/org/apache/harmony/pack200/SegmentHeader.java +++ src/main/java/org/apache/harmony/pack200/SegmentHeader.java @@ -0,0 +1,415 @@ +/* + * 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.EOFException; +import java.io.IOException; +import java.io.InputStream; + +public class SegmentHeader { + + private int archiveMajor; + + private int archiveMinor; + + private long archiveModtime; + + private long archiveSize; + + private int attributeDefinitionCount; + + private InputStream bandHeadersInputStream; + + private int bandHeadersSize; + + private int classCount; + + private int cpClassCount; + + private int cpDescriptorCount; + + private int cpDoubleCount; + + private int cpFieldCount; + + private int cpFloatCount; + + private int cpIMethodCount; + + private int cpIntCount; + + private int cpLongCount; + + private int cpMethodCount; + + private int cpSignatureCount; + + private int cpStringCount; + + private int cpUTF8Count; + + private int defaultClassMajorVersion; + + private int defaultClassMinorVersion; + + private int innerClassCount; + + private int numberOfFiles; + + private int segmentsRemaining; + + private SegmentOptions options; + + + /** + * The magic header for a Pack200 Segment is 0xCAFED00D. I wonder where they + * get their inspiration from ... + */ + private static final int[] magic = { 0xCA, 0xFE, 0xD0, 0x0D }; + + public void unpack(InputStream in) throws IOException, + Pack200Exception, Error, Pack200Exception { + long word[] = decodeScalar("archive_magic_word", in, Codec.BYTE1, + magic.length); + for (int m = 0; m < magic.length; m++) + if (word[m] != magic[m]) + throw new Error("Bad header"); + setArchiveMinorVersion((int) decodeScalar("archive_minver", in, + Codec.UNSIGNED5)); + setArchiveMajorVersion((int) decodeScalar("archive_majver", in, + Codec.UNSIGNED5)); + options = new SegmentOptions((int) decodeScalar("archive_options", in, + Codec.UNSIGNED5)); + parseArchiveFileCounts(in); + parseArchiveSpecialCounts(in); + parseCpCounts(in); + parseClassCounts(in); + + if (getBandHeadersSize() > 0) { + byte[] bandHeaders = new byte[(int) getBandHeadersSize()]; + readFully(in, bandHeaders); + setBandHeadersData(bandHeaders); + } + } + + /** + * Sets the minor version of this archive + * + * @param version + * the minor version of the archive + * @throws Pack200Exception + * if the minor version is not 7 + */ + private void setArchiveMinorVersion(int version) throws Pack200Exception { + if (version != 7) + throw new Pack200Exception("Invalid segment minor version"); + archiveMinor = version; + } + + /** + * Sets the major version of this archive. + * + * @param version + * the minor version of the archive + * @throws Pack200Exception + * if the major version is not 150 + */ + private void setArchiveMajorVersion(int version) throws Pack200Exception { + if (version != 150) + throw new Pack200Exception("Invalid segment major version"); + archiveMajor = version; + } + + public long getArchiveModtime() { + return archiveModtime; + } + + public int getArchiveMajor() { + return archiveMajor; + } + + public int getArchiveMinor() { + return archiveMinor; + } + + public int getAttributeDefinitionCount() { + return attributeDefinitionCount; + } + + public int getClassCount() { + return classCount; + } + + public int getCpClassCount() { + return cpClassCount; + } + + public int getCpDescriptorCount() { + return cpDescriptorCount; + } + + public int getCpDoubleCount() { + return cpDoubleCount; + } + + public int getCpFieldCount() { + return cpFieldCount; + } + + public int getCpFloatCount() { + return cpFloatCount; + } + + public int getCpIMethodCount() { + return cpIMethodCount; + } + + public int getCpIntCount() { + return cpIntCount; + } + + public int getCpLongCount() { + return cpLongCount; + } + + public int getCpMethodCount() { + return cpMethodCount; + } + + public int getCpSignatureCount() { + return cpSignatureCount; + } + + public int getCpStringCount() { + return cpStringCount; + } + + public int getCpUTF8Count() { + return cpUTF8Count; + } + + public int getDefaultClassMajorVersion() { + return defaultClassMajorVersion; + } + + public int getDefaultClassMinorVersion() { + return defaultClassMinorVersion; + } + + public int getInnerClassCount() { + return innerClassCount; + } + + public void setNumberOfFiles(int numberOfFiles) { + this.numberOfFiles = numberOfFiles; + } + + public long getArchiveSize() { + return archiveSize; + } + + /** + * Obtain the band headers data as an input stream. If no band headers are + * present, this will return an empty input stream to prevent any further + * reads taking place. + * + * Note that as a stream, data consumed from this input stream can't be + * re-used. Data is only read from this stream if the encoding is such that + * additional information needs to be decoded from the stream itself. + * + * @return the band headers input stream + */ + public InputStream getBandHeadersInputStream() { + if (bandHeadersInputStream == null) { + bandHeadersInputStream = new ByteArrayInputStream(new byte[0]); + } + return bandHeadersInputStream; + + } + + public int getNumberOfFiles() { + return numberOfFiles; + } + + public int getSegmentsRemaining() { + return segmentsRemaining; + } + + public SegmentOptions getOptions() { + return options; + } + + private void parseArchiveFileCounts(InputStream in) throws IOException, + Pack200Exception { + if (options.hasArchiveFileCounts()) { + setArchiveSize(decodeScalar("archive_size_hi", in, Codec.UNSIGNED5) << 32 + | decodeScalar("archive_size_lo", in, Codec.UNSIGNED5)); + setSegmentsRemaining(decodeScalar("archive_next_count", in, + Codec.UNSIGNED5)); + setArchiveModtime(decodeScalar("archive_modtime", in, + Codec.UNSIGNED5)); + numberOfFiles = (int)decodeScalar("file_count", in, Codec.UNSIGNED5); + } + } + + private void parseArchiveSpecialCounts(InputStream in) throws IOException, + Pack200Exception { + if (getOptions().hasSpecialFormats()) { + bandHeadersSize = (int)decodeScalar("band_headers_size", in, + Codec.UNSIGNED5); + setAttributeDefinitionCount(decodeScalar("attr_definition_count", + in, Codec.UNSIGNED5)); + } + } + + private void parseClassCounts(InputStream in) throws IOException, + Pack200Exception { + innerClassCount = (int)decodeScalar("ic_count", in, Codec.UNSIGNED5); + defaultClassMinorVersion = (int)decodeScalar("default_class_minver", in, + Codec.UNSIGNED5); + defaultClassMajorVersion = (int)decodeScalar("default_class_majver", in, + Codec.UNSIGNED5); + classCount = (int)decodeScalar("class_count", in, Codec.UNSIGNED5); + } + + private void parseCpCounts(InputStream in) throws IOException, + Pack200Exception { + cpUTF8Count = (int)decodeScalar("cp_Utf8_count", in, Codec.UNSIGNED5); + if (getOptions().hasCPNumberCounts()) { + cpIntCount = (int)decodeScalar("cp_Int_count", in, Codec.UNSIGNED5); + cpFloatCount = (int)decodeScalar("cp_Float_count", in, Codec.UNSIGNED5); + cpLongCount = (int)decodeScalar("cp_Long_count", in, Codec.UNSIGNED5); + cpDoubleCount = (int)decodeScalar("cp_Double_count", in, + Codec.UNSIGNED5); + } + cpStringCount = (int)decodeScalar("cp_String_count", in, Codec.UNSIGNED5); + cpClassCount = (int)decodeScalar("cp_Class_count", in, Codec.UNSIGNED5); + cpSignatureCount = (int)decodeScalar("cp_Signature_count", in, + Codec.UNSIGNED5); + cpDescriptorCount = (int)decodeScalar("cp_Descr_count", in, Codec.UNSIGNED5); + cpFieldCount = (int)decodeScalar("cp_Field_count", in, Codec.UNSIGNED5); + cpMethodCount = (int)decodeScalar("cp_Method_count", in, Codec.UNSIGNED5); + cpIMethodCount = (int)decodeScalar("cp_Imethod_count", in, Codec.UNSIGNED5); + } + + /** + * Decode a number of scalars from the band file. A scalar is like a band, + * but does not perform any band code switching. + * + * @param name + * the name of the scalar (primarily for logging/debugging + * purposes) + * @param in + * the input stream to read from + * @param codec + * the codec for this scalar + * @return an array of decoded long[] values + * @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 + */ + private long[] decodeScalar(String name, InputStream in, BHSDCodec codec, + int n) throws IOException, Pack200Exception { + // TODO Remove debugging code +// debug("Parsed #" + name + " (" + n + ")"); + return codec.decode(n, in); + } + + /** + * Decode a scalar from the band file. A scalar is like a band, but does not + * perform any band code switching. + * + * @param name + * the name of the scalar (primarily for logging/debugging + * purposes) + * @param in + * the input stream to read from + * @param codec + * the codec for this scalar + * @return the decoded value + * @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 + */ + private long decodeScalar(String name, InputStream in, BHSDCodec codec) + throws IOException, Pack200Exception { +// debug("Parsed #" + name + " (1)"); + return codec.decode(in); + } + + public void setArchiveModtime(long archiveModtime) { + this.archiveModtime = archiveModtime; + } + + public void setArchiveSize(long archiveSize) { + this.archiveSize = archiveSize; + } + + private void setAttributeDefinitionCount(long valuie) { + this.attributeDefinitionCount = (int) valuie; + } + + private void setBandHeadersData(byte[] bandHeaders) { + this.bandHeadersInputStream = new ByteArrayInputStream(bandHeaders); + } + + public void setSegmentsRemaining(long value) { + segmentsRemaining = (int) value; + } + + /** + * Completely reads in a byte array, akin to the implementation in + * {@link java.lang.DataInputStream}. TODO Refactor out into a separate + * InputStream handling class + * + * @param in + * the input stream to read from + * @param data + * the byte array to read into + * @throws IOException + * if a problem occurs during reading from the underlying stream + * @throws Pack200Exception + * if a problem occurs with an unexpected value or unsupported + * codec + */ + private static void readFully(InputStream in, byte[] data) + throws IOException, Pack200Exception { + int total = in.read(data); + if (total == -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( + "Failed to read some data from input stream"); + total += delta; + } + } + + public int getBandHeadersSize() { + return bandHeadersSize; + } + + +} Index: src/test/java/org/apache/harmony/pack200/tests/AttributeLayoutTest.java =================================================================== --- src/test/java/org/apache/harmony/pack200/tests/AttributeLayoutTest.java (revision 565676) +++ src/test/java/org/apache/harmony/pack200/tests/AttributeLayoutTest.java (working copy) @@ -23,6 +23,7 @@ import org.apache.harmony.pack200.AttributeLayout; import org.apache.harmony.pack200.Pack200Exception; import org.apache.harmony.pack200.Segment; +import org.apache.harmony.pack200.SegmentConstantPool; public class AttributeLayoutTest extends TestCase { public class TestSegment extends Segment { @@ -32,7 +33,7 @@ { "Zero", "One" }, // UTF-8 { "Ein", "Zwei" }, // Signature }; - return new SegmentConstantPool() { + return new SegmentConstantPool(null) { public Object getValue(int cp, long index) { if (index == -1) return null; @@ -55,9 +56,9 @@ public void testLayoutRU() throws Pack200Exception { AttributeLayout layout = new AttributeLayout("RU",AttributeLayout.CONTEXT_CLASS,"RU", 1); Segment segment = new TestSegment(); - assertNull(layout.getValue(-1, segment)); - assertEquals("Zero",layout.getValue(0, segment)); - assertEquals("One",layout.getValue(1, segment)); + assertNull(layout.getValue(-1, segment.getConstantPool())); + assertEquals("Zero",layout.getValue(0, segment.getConstantPool())); + assertEquals("One",layout.getValue(1, segment.getConstantPool())); } public void testLayoutRUN() throws Pack200Exception { AttributeLayout layout = new AttributeLayout("RUN",AttributeLayout.CONTEXT_CLASS,"RUN", 1); @@ -62,9 +63,9 @@ public void testLayoutRUN() throws Pack200Exception { AttributeLayout layout = new AttributeLayout("RUN",AttributeLayout.CONTEXT_CLASS,"RUN", 1); Segment segment = new TestSegment(); - assertNull(layout.getValue(0, segment)); - assertEquals("Zero",layout.getValue(1, segment)); - assertEquals("One",layout.getValue(2, segment)); + assertNull(layout.getValue(0, segment.getConstantPool())); + assertEquals("Zero",layout.getValue(1, segment.getConstantPool())); + assertEquals("One",layout.getValue(2, segment.getConstantPool())); } public void testLayoutRS() throws Pack200Exception { AttributeLayout layout = new AttributeLayout("RS",AttributeLayout.CONTEXT_CLASS,"RS", 1); @@ -69,9 +70,9 @@ public void testLayoutRS() throws Pack200Exception { AttributeLayout layout = new AttributeLayout("RS",AttributeLayout.CONTEXT_CLASS,"RS", 1); Segment segment = new TestSegment(); - assertNull(layout.getValue(-1, segment)); - assertEquals("Ein",layout.getValue(0, segment)); - assertEquals("Zwei",layout.getValue(1, segment)); + assertNull(layout.getValue(-1, segment.getConstantPool())); + assertEquals("Ein",layout.getValue(0, segment.getConstantPool())); + assertEquals("Zwei",layout.getValue(1, segment.getConstantPool())); } public void testLayoutRSN() throws Pack200Exception { AttributeLayout layout = new AttributeLayout("RSN",AttributeLayout.CONTEXT_CLASS,"RSN", 1); @@ -76,9 +77,9 @@ public void testLayoutRSN() throws Pack200Exception { AttributeLayout layout = new AttributeLayout("RSN",AttributeLayout.CONTEXT_CLASS,"RSN", 1); Segment segment = new TestSegment(); - assertNull(layout.getValue(0, segment)); - assertEquals("Ein",layout.getValue(1, segment)); - assertEquals("Zwei",layout.getValue(2, segment)); + assertNull(layout.getValue(0, segment.getConstantPool())); + assertEquals("Ein",layout.getValue(1, segment.getConstantPool())); + assertEquals("Zwei",layout.getValue(2, segment.getConstantPool())); } public boolean throwsException(String name, int context, String layout) { try { Index: src/test/java/org/apache/harmony/pack200/tests/BandSetTest.java =================================================================== --- src/test/java/org/apache/harmony/pack200/tests/BandSetTest.java +++ src/test/java/org/apache/harmony/pack200/tests/BandSetTest.java @@ -0,0 +1,88 @@ +/* + * 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.tests; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import junit.framework.TestCase; + +import org.apache.harmony.pack200.BHSDCodec; +import org.apache.harmony.pack200.BandSet; +import org.apache.harmony.pack200.Codec; +import org.apache.harmony.pack200.Pack200Exception; +import org.apache.harmony.pack200.Segment; + +public class BandSetTest extends TestCase { + + private BandSet bandSet = new BandSet(new Segment()) { + + public void pack(OutputStream outputStream) { + // TODO Auto-generated method stub + + } + + public void unpack(InputStream inputStream) throws IOException, Pack200Exception { + // TODO Auto-generated method stub + + } + }; + + public void testDecodeBandInt() throws IOException, Pack200Exception { + BHSDCodec codec = Codec.BYTE1; + byte[] bytes = new byte[]{(byte)3,(byte)56,(byte)122,(byte)78}; + InputStream in = new ByteArrayInputStream(bytes); + int[] ints = bandSet.decodeBandInt("Test Band", in, codec, 4); + for (int i = 0; i < ints.length; i++) { + assertEquals("Wrong value in position " + i, ints[i], bytes[i]); + } + } + + public void testDecodeBandLong() throws IOException, Pack200Exception { + BHSDCodec codec = Codec.BYTE1; + byte[] bytes = new byte[]{(byte)3,(byte)56,(byte)122,(byte)78, Byte.MAX_VALUE, Byte.MIN_VALUE}; + InputStream in = new ByteArrayInputStream(bytes); + long[] longs = bandSet.decodeBandLong("Test Band", in, codec, 4); + for (int i = 0; i < longs.length; i++) { + assertEquals("Wrong value in position " + i, longs[i], bytes[i]); + } + //TODO: Should test this with other Codecs. + } + + public void testParseFlags1() { + + } + + public void testParseFlags2() { + + } + + public void testParseFlags3() { + + } + + public void testParseReferences1() { + + } + + public void testParseReferences2() { + + } + +}