Index: src/main/java/org/apache/harmony/pack200/AttrDefinitionBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/AttrDefinitionBands.java (revision 583385) +++ src/main/java/org/apache/harmony/pack200/AttrDefinitionBands.java (working copy) @@ -18,7 +18,6 @@ import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; /** * @@ -41,14 +40,6 @@ } /* (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, @@ -73,9 +64,11 @@ if(index == -1) { index = overflowIndex++; } - attributeDefinitionMap.add(new AttributeLayout( + AttributeLayout layout = new AttributeLayout( attributeDefinitionName[i], context, - attributeDefinitionLayout[i], index)); + attributeDefinitionLayout[i], index, false); + NewAttributeBands newBands = new NewAttributeBands(segment, layout); + attributeDefinitionMap.add(layout, newBands); } attributeDefinitionMap.checkMap(); } Index: src/main/java/org/apache/harmony/pack200/AttributeLayout.java =================================================================== --- src/main/java/org/apache/harmony/pack200/AttributeLayout.java (revision 596578) +++ src/main/java/org/apache/harmony/pack200/AttributeLayout.java (working copy) @@ -98,17 +98,34 @@ private long mask; private String name; + private boolean isDefault; + private int backwardsCallCount; + + /** + * Construct a default AttributeLayout + * (equivalent to new AttributeLayout(name, context, layout, index, true);) + * @param name + * @param context + * @param layout + * @param index + * @throws Pack200Exception + */ public AttributeLayout(String name, int context, String layout, int index) throws Pack200Exception { - super(); + this(name, context, layout, index, true); + } + + public AttributeLayout(String name, int context, String layout, int index, + boolean isDefault) throws Pack200Exception { + super(); this.index = index; this.context = context; - if (index >= 0) { - this.mask = 1L << index; - } else { - this.mask = 0; - } + if (index >= 0) { + this.mask = 1L << index; + } else { + this.mask = 0; + } if (context != CONTEXT_CLASS && context != CONTEXT_CODE && context != CONTEXT_FIELD && context != CONTEXT_METHOD) throw new Pack200Exception("Attribute context out of range: " @@ -113,13 +130,14 @@ && context != CONTEXT_FIELD && context != CONTEXT_METHOD) throw new Pack200Exception("Attribute context out of range: " + context); - if (layout == null) // || layout.length() == 0) - throw new Pack200Exception("Cannot have a null layout"); + if (layout == null) // || layout.length() == 0) + throw new Pack200Exception("Cannot have a null layout"); if (name == null || name.length() == 0) - throw new Pack200Exception("Cannot have an unnamed layout"); + throw new Pack200Exception("Cannot have an unnamed layout"); this.name = name; - this.layout = layout; - } + this.layout = layout; + this.isDefault = isDefault; + } public boolean equals(Object obj) { @@ -243,18 +261,19 @@ public int numBackwardsCallables() { if(layout == "*") { - return 1; // TODO: complicated attributes (shouldn't be *'s at all...) + return 1; + } else { + return backwardsCallCount; } - int num = 0; - String[] split = layout.split("\\("); - if(split.length > 0) { - for (int i = 1; i < split.length; i++) { - if(split[i].startsWith("-") || split[i].startsWith("0")) { - num++; - } - } - } - return num; + } + + + public boolean isDefaultLayout() { + return isDefault; + } + + public void setBackwardsCallCount(int backwardsCallCount) { + this.backwardsCallCount = backwardsCallCount; } } Index: src/main/java/org/apache/harmony/pack200/AttributeLayoutMap.java =================================================================== --- src/main/java/org/apache/harmony/pack200/AttributeLayoutMap.java (revision 596578) +++ src/main/java/org/apache/harmony/pack200/AttributeLayoutMap.java (working copy) @@ -198,6 +198,8 @@ // the value of their context constants (AttributeLayout.CONTEXT_CLASS etc.) private final Map[] layouts = new Map[] {classLayouts, fieldLayouts, methodLayouts, codeLayouts}; + private final Map layoutsToBands = new HashMap(); + public AttributeLayoutMap() throws Pack200Exception { AttributeLayout[] defaultAttributeLayouts = getDefaultAttributeLayouts(); for (int i = 0; i < defaultAttributeLayouts.length; i++) { @@ -208,6 +210,13 @@ public void add(AttributeLayout layout) { layouts[layout.getContext()].put(new Integer(layout.getIndex()), layout); } + + + + public void add(AttributeLayout layout, NewAttributeBands newBands) { + add(layout); + layoutsToBands.put(layout, newBands); + } public AttributeLayout getAttributeLayout(String name, int context) throws Pack200Exception { @@ -258,5 +267,9 @@ } } } + + public NewAttributeBands getAttributeBands(AttributeLayout layout) { + return (NewAttributeBands) layoutsToBands.get(layout); + } } Index: src/main/java/org/apache/harmony/pack200/BandSet.java =================================================================== --- src/main/java/org/apache/harmony/pack200/BandSet.java (revision 608793) +++ src/main/java/org/apache/harmony/pack200/BandSet.java (working copy) @@ -19,12 +19,17 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; +import org.apache.harmony.pack200.bytecode.CPClass; import org.apache.harmony.pack200.bytecode.CPDouble; +import org.apache.harmony.pack200.bytecode.CPFieldRef; import org.apache.harmony.pack200.bytecode.CPFloat; import org.apache.harmony.pack200.bytecode.CPInteger; +import org.apache.harmony.pack200.bytecode.CPInterfaceMethodRef; import org.apache.harmony.pack200.bytecode.CPLong; +import org.apache.harmony.pack200.bytecode.CPMethodRef; +import org.apache.harmony.pack200.bytecode.CPNameAndType; +import org.apache.harmony.pack200.bytecode.CPString; import org.apache.harmony.pack200.bytecode.CPUTF8; import org.apache.harmony.pack200.bytecode.ClassConstantPool; @@ -32,8 +37,6 @@ public abstract void unpack(InputStream inputStream) throws IOException, Pack200Exception; - public abstract void pack(OutputStream outputStream); - protected Segment segment; protected SegmentHeader header; @@ -486,4 +489,105 @@ return result; } + public CPString[] parseCPStringReferences(String name, InputStream in, BHSDCodec codec, int count) throws IOException, Pack200Exception { + String[] reference = segment.getCpBands().getCpString(); + int[] indices = decodeBandInt(name, in, codec, count, reference.length - 1); + CPString[] result = new CPString[indices.length]; + for (int i1 = 0; i1 < count; i1++) { + int index = indices[i1]; + if (index < 0 || index >= reference.length) + throw new Pack200Exception( + "Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length); + result[i1] = new CPString(reference[index]); + } + return result; + } + + public CPInterfaceMethodRef[] parseCPInterfaceMethodRefReferences(String name, InputStream in, BHSDCodec codec, int count) throws IOException, Pack200Exception { + String[] reference = segment.getCpBands().getCpIMethodClass(); + String[] descriptors = segment.getCpBands().getCpIMethodDescriptor(); + int[] indices = decodeBandInt(name, in, codec, count, reference.length - 1); + CPInterfaceMethodRef[] result = new CPInterfaceMethodRef[indices.length]; + for (int i1 = 0; i1 < count; i1++) { + int index = indices[i1]; + if (index < 0 || index >= reference.length) + throw new Pack200Exception( + "Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length); + result[i1] = new CPInterfaceMethodRef(reference[index], descriptors[index]); + } + return result; + } + + public CPMethodRef[] parseCPMethodRefReferences(String name, InputStream in, BHSDCodec codec, int count) throws IOException, Pack200Exception { + String[] reference = segment.getCpBands().getCpMethodClass(); + String[] descriptors = segment.getCpBands().getCpMethodDescriptor(); + int[] indices = decodeBandInt(name, in, codec, count, reference.length - 1); + CPMethodRef[] result = new CPMethodRef[indices.length]; + for (int i1 = 0; i1 < count; i1++) { + int index = indices[i1]; + if (index < 0 || index >= reference.length) + throw new Pack200Exception( + "Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length); + result[i1] = new CPMethodRef(reference[index], descriptors[index]); + } + return result; + } + + public CPFieldRef[] parseCPFieldRefReferences(String name, InputStream in, BHSDCodec codec, int count) throws IOException, Pack200Exception { + String[] reference = segment.getCpBands().getCpFieldClass(); + String[] descriptors = segment.getCpBands().getCpFieldDescriptor(); + int[] indices = decodeBandInt(name, in, codec, count, reference.length - 1); + CPFieldRef[] result = new CPFieldRef[indices.length]; + for (int i1 = 0; i1 < count; i1++) { + int index = indices[i1]; + if (index < 0 || index >= reference.length) + throw new Pack200Exception( + "Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length); + result[i1] = new CPFieldRef(reference[index], descriptors[index]); + } + return result; + } + + public CPNameAndType[] parseCPDescriptorReferences(String name, InputStream in, BHSDCodec codec, int count) throws IOException, Pack200Exception { + String[] reference = segment.getCpBands().getCpDescriptor(); + int[] indices = decodeBandInt(name, in, codec, count, reference.length - 1); + CPNameAndType[] result = new CPNameAndType[indices.length]; + for (int i1 = 0; i1 < count; i1++) { + int index = indices[i1]; + if (index < 0 || index >= reference.length) + throw new Pack200Exception( + "Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length); + result[i1] = new CPNameAndType(reference[index]); + } + return result; + } + + public CPUTF8[] parseCPSignatureReferences(String name, InputStream in, BHSDCodec codec, int count) throws IOException, Pack200Exception { + String[] reference = segment.getCpBands().getCpSignature(); + int[] indices = decodeBandInt(name, in, codec, count, reference.length - 1); + CPUTF8[] result = new CPUTF8[indices.length]; + for (int i1 = 0; i1 < count; i1++) { + int index = indices[i1]; + if (index < 0 || index >= reference.length) + throw new Pack200Exception( + "Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length); + result[i1] = new CPUTF8(reference[index], ClassConstantPool.DOMAIN_UNDEFINED); + } + return result; + } + + public CPClass[] parseCPClassReferences(String name, InputStream in, BHSDCodec codec, int count) throws IOException, Pack200Exception { + String[] reference = segment.getCpBands().getCpClass(); + int[] indices = decodeBandInt(name, in, codec, count, reference.length - 1); + CPClass[] result = new CPClass[indices.length]; + for (int i1 = 0; i1 < count; i1++) { + int index = indices[i1]; + if (index < 0 || index >= reference.length) + throw new Pack200Exception( + "Something has gone wrong during parsing references, index = " + index + ", array size = " + reference.length); + result[i1] = new CPClass(reference[index]); + } + return result; + } + } Index: src/main/java/org/apache/harmony/pack200/BcBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/BcBands.java (revision 608793) +++ src/main/java/org/apache/harmony/pack200/BcBands.java (working copy) @@ -19,7 +19,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.List; @@ -27,7 +26,6 @@ import org.apache.harmony.pack200.bytecode.BCIRenumberedAttribute; import org.apache.harmony.pack200.bytecode.ByteCode; import org.apache.harmony.pack200.bytecode.CodeAttribute; -import org.apache.harmony.pack200.bytecode.LineNumberTableAttribute; import org.apache.harmony.pack200.bytecode.OperandManager; /** @@ -73,14 +71,6 @@ } /* (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, Index: src/main/java/org/apache/harmony/pack200/ClassBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/ClassBands.java (revision 608793) +++ src/main/java/org/apache/harmony/pack200/ClassBands.java (working copy) @@ -18,9 +18,9 @@ import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import org.apache.harmony.pack200.IcBands.ICTuple; import org.apache.harmony.pack200.bytecode.Attribute; @@ -25,9 +25,11 @@ import org.apache.harmony.pack200.IcBands.ICTuple; import org.apache.harmony.pack200.bytecode.Attribute; import org.apache.harmony.pack200.bytecode.CPClass; +import org.apache.harmony.pack200.bytecode.CPNameAndType; import org.apache.harmony.pack200.bytecode.CPUTF8; import org.apache.harmony.pack200.bytecode.ClassConstantPool; import org.apache.harmony.pack200.bytecode.ConstantValueAttribute; +import org.apache.harmony.pack200.bytecode.EnclosingMethodAttribute; import org.apache.harmony.pack200.bytecode.ExceptionsAttribute; import org.apache.harmony.pack200.bytecode.LineNumberTableAttribute; import org.apache.harmony.pack200.bytecode.LocalVariableTableAttribute; @@ -36,7 +38,7 @@ import org.apache.harmony.pack200.bytecode.SourceFileAttribute; /** - * + * Pack200 Class Bands */ public class ClassBands extends BandSet { @@ -106,16 +108,6 @@ /* * (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 { @@ -143,6 +135,10 @@ Pack200Exception { fieldDescr = parseReferences("field_descr", in, Codec.DELTA5, classFieldCount, cpBands.getCpDescriptor()); + parseFieldAttrBands(in); + } + + private void parseFieldAttrBands(InputStream in) throws IOException, Pack200Exception { fieldFlags = parseFlags("field_flags", in, classFieldCount, Codec.UNSIGNED5, options.hasFieldFlagsHi()); int fieldAttrCount = SegmentUtils.countBit16(fieldFlags); @@ -154,6 +150,15 @@ AttributeLayout.CONTEXT_FIELD); int[] fieldAttrCalls = decodeBandInt("field_attr_calls", in, Codec.UNSIGNED5, callCount); + + // Assign empty field attributes + 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(); + } + } AttributeLayout constantValueLayout = attrMap.getAttributeLayout( "ConstantValue", AttributeLayout.CONTEXT_FIELD); @@ -172,11 +177,38 @@ Codec.UNSIGNED5, signatureCount); int signatureIndex = 0; - fieldAttributes = new ArrayList[classCount][]; + int backwardsCallsUsed = parseFieldMetadataBands(in, fieldAttrCalls); + + // Parse non-predefined attribute bands + int backwardsCallIndex = backwardsCallsUsed; + int limit = options.hasFieldFlagsHi() ? 62 : 31; + AttributeLayout[] otherLayouts = new AttributeLayout[limit + 1]; + int[] counts = new int[limit + 1]; + List[] otherAttributes = new List[limit + 1]; + for (int i = 0; i < limit; i++) { + AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_FIELD); + if(layout != null && !(layout.isDefaultLayout())) { + otherLayouts[i] = layout; + counts[i] = SegmentUtils.countMatches(fieldFlags, + layout); + } + } + for (int i = 0; i < counts.length; i++) { + if(counts[i] > 0) { + NewAttributeBands bands = attrMap.getAttributeBands(otherLayouts[i]); + otherAttributes[i] = bands.parseAttributes(in, counts[i]); + int numBackwardsCallables = otherLayouts[i].numBackwardsCallables(); + if(numBackwardsCallables > 0) { + int[] backwardsCalls = new int[numBackwardsCallables]; + System.arraycopy(fieldAttrCalls, backwardsCallIndex, backwardsCalls, 0, numBackwardsCallables); + bands.setBackwardsCalls(backwardsCalls); + backwardsCallIndex+= numBackwardsCallables; + } + } + } + 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 (constantValueLayout.matches(flag)) { // we've got a value to read @@ -205,11 +237,16 @@ .add(new SignatureAttribute(value)); signatureIndex++; } + // Non-predefined attributes + for (int k = 0; k < otherLayouts.length; k++) { + if(otherLayouts[k] != null && otherLayouts[k].matches(flag)) { + // Add the next attribute + fieldAttributes[i][j].add(otherAttributes[k].get(0)); + otherAttributes[k].remove(0); + } + } } } - parseFieldMetadataBands(in, fieldAttrCalls); - - // TODO: Parse other attribute bands } private void parseMethodBands(InputStream in) throws IOException, @@ -215,10 +252,13 @@ private void parseMethodBands(InputStream in) throws IOException, Pack200Exception { methodDescr = parseReferences("method_descr", in, Codec.MDELTA5, - classMethodCount, cpBands.getCpDescriptor()); + classMethodCount, cpBands.getCpDescriptor()); + parseMethodAttrBands(in); + } + + private void parseMethodAttrBands(InputStream in) throws IOException, Pack200Exception { methodFlags = parseFlags("method_flags", in, classMethodCount, Codec.UNSIGNED5, options.hasMethodFlagsHi()); - int methodAttrCount = SegmentUtils.countBit16(methodFlags); int[] methodAttrCounts = decodeBandInt("method_attr_count", in, Codec.UNSIGNED5, methodAttrCount); @@ -228,7 +268,7 @@ AttributeLayout.CONTEXT_METHOD); methodAttrCalls = decodeBandInt("code_attr_calls", in, Codec.UNSIGNED5, callCount); - + // assign empty method attributes methodAttributes = new ArrayList[classCount][]; for (int i = 0; i < classCount; i++) { @@ -237,11 +277,98 @@ methodAttributes[i][j] = new ArrayList(); } } - parseAttributeMethodExceptions(in); - parseAttributeMethodSigntaure(in); - parseMethodMetadataBands(in, methodAttrCalls); + + // Parse method exceptions attributes + AttributeLayout methodExceptionsLayout = attrMap.getAttributeLayout(AttributeLayout.ATTRIBUTE_EXCEPTIONS, + AttributeLayout.CONTEXT_METHOD); + int count = SegmentUtils.countMatches(methodFlags, methodExceptionsLayout); + int[] numExceptions = decodeBandInt("method_Exceptions_n", in, + Codec.UNSIGNED5, count); + String[][] methodExceptionsRS = parseReferences("method_Exceptions_RC", + in, Codec.UNSIGNED5, numExceptions, cpBands.getCpClass()); + + // Parse method signature attributes + AttributeLayout methodSignatureLayout = attrMap.getAttributeLayout( + AttributeLayout.ATTRIBUTE_SIGNATURE, + AttributeLayout.CONTEXT_METHOD); + int count1 = SegmentUtils.countMatches(methodFlags, methodSignatureLayout); + long[] methodSignatureRS = decodeBandLong("method_signature_RS", in, + Codec.UNSIGNED5, count1); + + // Parse method metadata bands + int backwardsCallsUsed = parseMethodMetadataBands(in, methodAttrCalls); + + // Parse non-predefined attribute bands + int backwardsCallIndex = backwardsCallsUsed; + int limit = options.hasMethodFlagsHi() ? 62 : 31; + AttributeLayout[] otherLayouts = new AttributeLayout[limit + 1]; + int[] counts = new int[limit + 1]; + List[] otherAttributes = new List[limit + 1]; + for (int i = 0; i < limit; i++) { + AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_METHOD); + if(layout != null && !(layout.isDefaultLayout())) { + otherLayouts[i] = layout; + counts[i] = SegmentUtils.countMatches(methodFlags, + layout); + } + } + for (int i = 0; i < counts.length; i++) { + if(counts[i] > 0) { + NewAttributeBands bands = attrMap.getAttributeBands(otherLayouts[i]); + otherAttributes[i] = bands.parseAttributes(in, counts[i]); + int numBackwardsCallables = otherLayouts[i].numBackwardsCallables(); + if(numBackwardsCallables > 0) { + int[] backwardsCalls = new int[numBackwardsCallables]; + System.arraycopy(methodAttrCalls, backwardsCallIndex, backwardsCalls, 0, numBackwardsCallables); + bands.setBackwardsCalls(backwardsCalls); + backwardsCallIndex+= numBackwardsCallables; + } + } + } - // TODO: Parse other attribute bands + // Add attributes to the attribute arrays + int methodExceptionsIndex = 0; + int methodSignatureIndex = 0; + for (int i = 0; i < methodAttributes.length; i++) { + for (int j = 0; j < methodAttributes[i].length; j++) { + long flag = methodFlags[i][j]; + if (methodExceptionsLayout.matches(flag)) { + int n = numExceptions[methodExceptionsIndex]; + String[] exceptions = methodExceptionsRS[methodExceptionsIndex]; + CPClass[] exceptionClasses = new CPClass[n]; + for (int k = 0; k < n; k++) { + exceptionClasses[k] = new CPClass(exceptions[k]); + } + methodAttributes[i][j].add(new ExceptionsAttribute( + exceptionClasses)); + methodExceptionsIndex++; + } + if (methodSignatureLayout.matches(flag)) { + // We've got a signature attribute + long result = methodSignatureRS[methodSignatureIndex]; + String desc = methodDescr[i][j]; + int colon = desc.indexOf(':'); + 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 = methodSignatureLayout.getValue(result, type, cpBands + .getConstantPool()); + methodAttributes[i][j] + .add(new ConstantValueAttribute(value)); + methodSignatureIndex++; + } + // Non-predefined attributes + for (int k = 0; k < otherLayouts.length; k++) { + if(otherLayouts[k] != null && otherLayouts[k].matches(flag)) { + // Add the next attribute + methodAttributes[i][j].add(otherAttributes[k].get(0)); + otherAttributes[k].remove(0); + } + } + } + } } private int getCallCount(int[][] methodAttrIndexes, long[][] flags, @@ -270,71 +397,6 @@ return callCount; } - private void parseAttributeMethodSigntaure(InputStream in) - throws IOException, Pack200Exception { - AttributeLayout layout = attrMap.getAttributeLayout( - AttributeLayout.ATTRIBUTE_SIGNATURE, - AttributeLayout.CONTEXT_METHOD); - int count = SegmentUtils.countMatches(methodFlags, layout); - long[] methodSignatureRS = decodeBandLong("method_signature_RS", in, - Codec.UNSIGNED5, count); - int index = 0; - for (int i = 0; i < methodAttributes.length; i++) { - for (int j = 0; j < methodAttributes[i].length; j++) { - long flag = methodFlags[i][j]; - if (layout.matches(flag)) { - // we've got a signature attribute - long result = methodSignatureRS[index]; - String desc = methodDescr[i][j]; - int colon = desc.indexOf(':'); - 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()); - methodAttributes[i][j] - .add(new ConstantValueAttribute(value)); - index++; - } - } - } - } - - /** - * @param in - * @throws Pack200Exception - * @throws IOException - */ - private void parseAttributeMethodExceptions(InputStream in) - throws Pack200Exception, IOException { - AttributeLayout layout = attrMap.getAttributeLayout("Exceptions", - AttributeLayout.CONTEXT_METHOD); - int count = SegmentUtils.countMatches(methodFlags, layout); - int[] numExceptions = decodeBandInt("method_Exceptions_n", in, - Codec.UNSIGNED5, count); - String[][] methodExceptionsRS = parseReferences("method_Exceptions_RC", - in, Codec.UNSIGNED5, numExceptions, cpBands.getCpClass()); - int index = 0; - for (int i = 0; i < classCount; i++) { - for (int j = 0; j < methodFlags[i].length; j++) { - long flag = methodFlags[i][j]; - if (layout.matches(flag)) { - int n = numExceptions[index]; - String[] exceptions = methodExceptionsRS[index]; - CPClass[] exceptionClasses = new CPClass[n]; - for (int k = 0; k < n; k++) { - exceptionClasses[k] = new CPClass(exceptions[k]); - } - methodAttributes[i][j].add(new ExceptionsAttribute( - exceptionClasses)); - index++; - } - } - } - } - private void parseClassAttrBands(InputStream in) throws IOException, Pack200Exception { String[] cpUTF8 = cpBands.getCpUTF8(); @@ -371,10 +433,11 @@ AttributeLayout.CONTEXT_CLASS); int enclosingMethodCount = SegmentUtils.countMatches(classFlags, enclosingMethodLayout); - int[] enclosingMethodRC = decodeBandInt("class_EnclosingMethod_RC", in, - Codec.UNSIGNED5, enclosingMethodCount); - int[] enclosingMethodRDN = decodeBandInt("class_EnclosingMethod_RDN", - in, Codec.UNSIGNED5, enclosingMethodCount); + String[] enclosingMethodRC = parseReferences( + "class_EnclosingMethod_RC", in, Codec.UNSIGNED5, + enclosingMethodCount, cpClass); + String[] enclosingMethodRDN = parseReferences( + "class_EnclosingMethod_RDN", in, Codec.UNSIGNED5, enclosingMethodCount, cpBands.getCpDescriptor()); AttributeLayout signatureLayout = attrMap.getAttributeLayout( AttributeLayout.ATTRIBUTE_SIGNATURE, @@ -384,7 +447,7 @@ int[] classSignature = decodeBandInt("class_Signature_RS", in, Codec.UNSIGNED5, signatureCount); - parseClassMetadataBands(in, classAttrCalls); + int backwardsCallsUsed = parseClassMetadataBands(in, classAttrCalls); AttributeLayout innerClassLayout = attrMap.getAttributeLayout( AttributeLayout.ATTRIBUTE_INNER_CLASSES, @@ -425,7 +488,34 @@ int defaultVersionMajor = header.getDefaultClassMajorVersion(); int defaultVersionMinor = header.getDefaultClassMinorVersion(); - // TODO: Parse other attribute bands + + // Parse non-predefined attribute bands + int backwardsCallIndex = backwardsCallsUsed; + int limit = options.hasClassFlagsHi() ? 62 : 31; + AttributeLayout[] otherLayouts = new AttributeLayout[limit + 1]; + int[] counts = new int[limit + 1]; + List[] otherAttributes = new List[limit + 1]; + for (int i = 0; i < limit; i++) { + AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_CLASS); + if(layout != null && !(layout.isDefaultLayout())) { + otherLayouts[i] = layout; + counts[i] = SegmentUtils.countMatches(classFlags, + layout); + } + } + for (int i = 0; i < counts.length; i++) { + if(counts[i] > 0) { + NewAttributeBands bands = attrMap.getAttributeBands(otherLayouts[i]); + otherAttributes[i] = bands.parseAttributes(in, counts[i]); + int numBackwardsCallables = otherLayouts[i].numBackwardsCallables(); + if(numBackwardsCallables > 0) { + int[] backwardsCalls = new int[numBackwardsCallables]; + System.arraycopy(classAttrCalls, backwardsCallIndex, backwardsCalls, 0, numBackwardsCallables); + bands.setBackwardsCalls(backwardsCalls); + backwardsCallIndex+= numBackwardsCallables; + } + } + } // Now process the attribute bands we have parsed int sourceFileIndex = 0; @@ -468,7 +558,10 @@ sourceFileIndex++; } if (enclosingMethodLayout.matches(flag)) { - // long result = + CPClass theClass = new CPClass(enclosingMethodRC[enclosingMethodIndex]); + CPNameAndType theMethod = new CPNameAndType(enclosingMethodRDN[enclosingMethodIndex]); + classAttributes[i].add(new EnclosingMethodAttribute(theClass, theMethod)); + enclosingMethodIndex++; } if (signatureLayout.matches(flag)) { long result = classSignature[signatureIndex]; @@ -514,6 +607,14 @@ classVersionMajor[i] = defaultVersionMajor; classVersionMinor[i] = defaultVersionMinor; } + // Non-predefined attributes + for (int j = 0; j < otherLayouts.length; j++) { + if(otherLayouts[j] != null && otherLayouts[j].matches(flag)) { + // Add the next attribute + classAttributes[i].add(otherAttributes[j].get(0)); + otherAttributes[j].remove(0); + } + } } } @@ -689,6 +790,34 @@ "code_LocalVariableTypeTable_slot", in, Codec.UNSIGNED5, localVariableTypeTableN); + // Parse non-predefined attribute bands + int backwardsCallIndex = 0; + int limit = options.hasCodeFlagsHi() ? 62 : 31; + AttributeLayout[] otherLayouts = new AttributeLayout[limit + 1]; + int[] counts = new int[limit + 1]; + List[] otherAttributes = new List[limit + 1]; + for (int i = 0; i < limit; i++) { + AttributeLayout layout = attrMap.getAttributeLayout(i, AttributeLayout.CONTEXT_CODE); + if(layout != null && !(layout.isDefaultLayout())) { + otherLayouts[i] = layout; + counts[i] = SegmentUtils.countMatches(codeFlags, + layout); + } + } + for (int i = 0; i < counts.length; i++) { + if(counts[i] > 0) { + NewAttributeBands bands = attrMap.getAttributeBands(otherLayouts[i]); + otherAttributes[i] = bands.parseAttributes(in, counts[i]); + int numBackwardsCallables = otherLayouts[i].numBackwardsCallables(); + if(numBackwardsCallables > 0) { + int[] backwardsCalls = new int[numBackwardsCallables]; + System.arraycopy(codeAttrCalls, backwardsCallIndex, backwardsCalls, 0, numBackwardsCallables); + bands.setBackwardsCalls(backwardsCalls); + backwardsCallIndex+= numBackwardsCallables; + } + } + } + int lineNumberIndex = 0; int lvtIndex = 0; int lvttIndex = 0; @@ -723,8 +852,16 @@ lvttIndex++; codeAttributes[i].add(lvtta); } + // Non-predefined attributes + for (int j = 0; j < otherLayouts.length; j++) { + if(otherLayouts[j] != null && otherLayouts[j].matches(codeFlags[i])) { + // Add the next attribute + codeAttributes[i].add(otherAttributes[j].get(0)); + otherAttributes[j].remove(0); + } + } } - // TODO: Parse other attribute bands + } private CPUTF8[][] stringsToCPUTF8(String[][] strings) { @@ -748,8 +885,9 @@ return cpUTF8s; } - private void parseFieldMetadataBands(InputStream in, int[] fieldAttrCalls) + private int parseFieldMetadataBands(InputStream in, int[] fieldAttrCalls) throws Pack200Exception, IOException { + int backwardsCallsUsed = 0; String[] RxA = new String[] { "RVA", "RIA" }; AttributeLayout rvaLayout = attrMap.getAttributeLayout( @@ -765,11 +903,14 @@ int[] backwardsCalls = new int[] {0, 0}; if(rvaCount > 0) { backwardsCalls[0] = fieldAttrCalls[0]; + backwardsCallsUsed++; if(riaCount > 0) { backwardsCalls[1] = fieldAttrCalls[1]; + backwardsCallsUsed++; } } else if (riaCount > 0) { backwardsCalls[1] = fieldAttrCalls[0]; + backwardsCallsUsed++; } MetadataBandGroup[] mb = parseMetadata(in, RxA, RxACount, backwardsCalls, "field"); Iterator rvaAttributesIterator = mb[0].getAttributes().iterator(); @@ -784,6 +925,7 @@ } } } + return backwardsCallsUsed; } private MetadataBandGroup[] parseMetadata(InputStream in, String[] RxA, int[] RxACount, @@ -884,8 +1026,9 @@ return mbg; } - private void parseMethodMetadataBands(InputStream in, int[] methodAttrCalls) + private int parseMethodMetadataBands(InputStream in, int[] methodAttrCalls) throws Pack200Exception, IOException { + int backwardsCallsUsed = 0; String[] RxA = new String[] { "RVA", "RIA", "RVPA", "RIPA", "AD" }; int[] rxaCounts = new int[] { 0, 0, 0, 0, 0 }; int[] backwardsCalls = new int[5]; @@ -892,6 +1035,7 @@ int methodAttrIndex = 0; for (int i = 0; i < backwardsCalls.length; i++) { if(rxaCounts[i] > 0) { + backwardsCallsUsed++; backwardsCalls[i] = methodAttrCalls[methodAttrIndex]; methodAttrIndex++; } else { @@ -936,9 +1080,19 @@ } } } + return backwardsCallsUsed; } - private void parseClassMetadataBands(InputStream in, int[] classAttrCalls) throws Pack200Exception, IOException { + /** + * Parse the class metadata bands and return the number of backwards callables + * @param in + * @param classAttrCalls + * @return + * @throws Pack200Exception + * @throws IOException + */ + private int parseClassMetadataBands(InputStream in, int[] classAttrCalls) throws Pack200Exception, IOException { + int numBackwardsCalls = 0; String[] RxA = new String[] { "RVA", "RIA" }; AttributeLayout rvaLayout = attrMap.getAttributeLayout( @@ -952,11 +1106,14 @@ int[] RxACount = new int[] { rvaCount, riaCount }; int[] backwardsCalls = new int[] {0, 0}; if(rvaCount > 0) { + numBackwardsCalls++; backwardsCalls[0] = classAttrCalls[0]; if(riaCount > 0) { + numBackwardsCalls++; backwardsCalls[1] = classAttrCalls[1]; } } else if (riaCount > 0) { + numBackwardsCalls++; backwardsCalls[1] = classAttrCalls[0]; } MetadataBandGroup[] mbgs = parseMetadata(in, RxA, RxACount, backwardsCalls, "class"); @@ -970,6 +1127,7 @@ classAttributes[i].add(riaAttributesIterator.next()); } } + return numBackwardsCalls; } public int[] getClassFieldCount() { Index: src/main/java/org/apache/harmony/pack200/CpBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/CpBands.java (revision 596606) +++ src/main/java/org/apache/harmony/pack200/CpBands.java (working copy) @@ -18,7 +18,6 @@ import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; public class CpBands extends BandSet { @@ -79,10 +78,6 @@ parseCpMethod(in); parseCpIMethod(in); } - - public void pack(OutputStream outputStream) { - - } /** * Parses the constant pool class names, using {@link #cpClassCount} to Index: src/main/java/org/apache/harmony/pack200/FileBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/FileBands.java (revision 591346) +++ src/main/java/org/apache/harmony/pack200/FileBands.java (working copy) @@ -18,7 +18,6 @@ import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; /** * Parses the file band headers (not including the actual bits themselves). @@ -51,14 +50,6 @@ } /* (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, Index: src/main/java/org/apache/harmony/pack200/IcBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/IcBands.java (revision 591346) +++ src/main/java/org/apache/harmony/pack200/IcBands.java (working copy) @@ -18,7 +18,6 @@ import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; /** * Pack200 Inner Class Bands @@ -50,14 +49,6 @@ } /* (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, Index: src/main/java/org/apache/harmony/pack200/bytecode/EnclosingMethodAttribute.java =================================================================== --- src/main/java/org/apache/harmony/pack200/bytecode/EnclosingMethodAttribute.java (revision 596578) +++ src/main/java/org/apache/harmony/pack200/bytecode/EnclosingMethodAttribute.java (working copy) @@ -24,12 +24,12 @@ private int class_index; private int method_index; private CPClass cpClass; - private CPMethod cpMethod; + private CPNameAndType method; - public EnclosingMethodAttribute(CPClass cpClass, CPMethod cpMethod) { + public EnclosingMethodAttribute(CPClass cpClass, CPNameAndType method) { super("EnclosingMethod"); this.cpClass = cpClass; - this.cpMethod = cpMethod; + this.method = method; } /* (non-Javadoc) @@ -43,8 +43,8 @@ super.resolve(pool); cpClass.resolve(pool); class_index = pool.indexOf(cpClass); - cpMethod.resolve(pool); - method_index = pool.indexOf(cpMethod); + method.resolve(pool); + method_index = pool.indexOf(method); } /* (non-Javadoc) Index: src/main/java/org/apache/harmony/pack200/bytecode/NewAttribute.java =================================================================== --- src/main/java/org/apache/harmony/pack200/bytecode/NewAttribute.java +++ src/main/java/org/apache/harmony/pack200/bytecode/NewAttribute.java @@ -0,0 +1,113 @@ +/* + * 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.bytecode; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * A New (i.e. non-predefined) Class File attribute + */ +public class NewAttribute extends Attribute { + + public NewAttribute(String attributeName) { + super(attributeName); + // TODO Auto-generated constructor stub + } + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.bytecode.Attribute#getLength() + */ + protected int getLength() { + // TODO Auto-generated method stub + return 0; + } + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.bytecode.Attribute#writeBody(java.io.DataOutputStream) + */ + protected void writeBody(DataOutputStream dos) throws IOException { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.bytecode.ClassFileEntry#toString() + */ + public String toString() { + // TODO Auto-generated method stub + return null; + } + + public void addInteger(int length, long value) { + + } + + public void addBCOffset(int length, long value) { + // TODO Auto-generated method stub + + } + + public void addBCIndex(int length, long value) { + // TODO Auto-generated method stub + + } + + public void addBCLength(int length, long value) { + // TODO Auto-generated method stub + + } + + public void addCPConstant(int length, CPConstant constant) { + // TODO Auto-generated method stub + + } + + public void addCPClass(int length, CPClass class1) { + // TODO Auto-generated method stub + + } + + public void addCPUTF8(int length, CPUTF8 cputf8) { + // TODO Auto-generated method stub + + } + + public void addCPNameAndType(int length, CPNameAndType type) { + // TODO Auto-generated method stub + + } + + public void addCPFieldRef(int length, CPFieldRef ref) { + // TODO Auto-generated method stub + + } + + public void addCPMethodRef(int length, CPMethodRef ref) { + // TODO Auto-generated method stub + + } + + public void addCPIMethodRef(int length, CPInterfaceMethodRef ref) { + // TODO Auto-generated method stub + + } + + + + +} Index: src/main/java/org/apache/harmony/pack200/NewAttributeBands.java =================================================================== --- src/main/java/org/apache/harmony/pack200/NewAttributeBands.java +++ src/main/java/org/apache/harmony/pack200/NewAttributeBands.java @@ -0,0 +1,920 @@ +/* + * 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.StringReader; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.harmony.pack200.bytecode.Attribute; +import org.apache.harmony.pack200.bytecode.CPClass; +import org.apache.harmony.pack200.bytecode.CPDouble; +import org.apache.harmony.pack200.bytecode.CPFieldRef; +import org.apache.harmony.pack200.bytecode.CPFloat; +import org.apache.harmony.pack200.bytecode.CPInteger; +import org.apache.harmony.pack200.bytecode.CPInterfaceMethodRef; +import org.apache.harmony.pack200.bytecode.CPLong; +import org.apache.harmony.pack200.bytecode.CPMethodRef; +import org.apache.harmony.pack200.bytecode.CPNameAndType; +import org.apache.harmony.pack200.bytecode.CPString; +import org.apache.harmony.pack200.bytecode.CPUTF8; +import org.apache.harmony.pack200.bytecode.NewAttribute; + +/** + * Set of bands relating to a non-predefined attribute + */ +public class NewAttributeBands extends BandSet { + + private AttributeLayout attributeLayout; + + private List attributes; + + private int backwardsCallCount; + + private List attributeLayoutElements; + + public NewAttributeBands(Segment segment, AttributeLayout attributeLayout) throws IOException { + super(segment); + this.attributeLayout = attributeLayout; + parseLayout(); + attributeLayout.setBackwardsCallCount(backwardsCallCount); + } + + /* + * (non-Javadoc) + * + * @see org.apache.harmony.pack200.BandSet#unpack(java.io.InputStream) + */ + public void unpack(InputStream in) throws IOException, Pack200Exception { + // does nothing - use parseAttributes instead + } + + /** + * Returns the list of attributes read in by this band set. This method + * should only be called after unpack() or it will return null. + * @return List of Attributes + */ + public List getAttributes() { + return attributes; + } + + /** + * Parse the bands relating to this AttributeLayout and return the correct + * class file attributes as a List of {@link Attribute} + * @throws Pack200Exception + */ + public List parseAttributes(InputStream in, int occurrenceCount) + throws IOException, Pack200Exception { + for (Iterator iter = attributeLayoutElements.iterator(); iter.hasNext();) { + AttributeLayoutElement element = (AttributeLayoutElement) iter.next(); + element.readBands(in, occurrenceCount); + } + + List attributes = new ArrayList(); + for (int i = 0; i < occurrenceCount; i++) { + attributes.add(getOneAttribute(i, attributeLayoutElements)); + } + return attributes; + } + + /** + * Get one attribute at the given index from the various bands. + * The correct bands must have already been read in. + * @param index + * @param elements + * @return + */ + private Attribute getOneAttribute(int index, List elements) { + NewAttribute attribute = new NewAttribute(attributeLayout.getName()); + for (Iterator iter = elements.iterator(); iter.hasNext();) { + AttributeLayoutElement element = (AttributeLayoutElement) iter.next(); + element.addToAttribute(index, attribute); + } + return attribute; + } + + /** + * Tokenise the layout into AttributeElements + * @return a List of AttributeElements + * @throws IOException + */ + private void parseLayout() throws IOException { + if(attributeLayoutElements == null) { + attributeLayoutElements = new ArrayList(); + StringReader stream = new StringReader(attributeLayout.getLayout()); + AttributeLayoutElement e; + while ((e = readNextAttributeElement(stream)) != null) { + attributeLayoutElements.add(e); + } + resolveCalls(); + } + } + + /** + * Resolve calls in the attribute layout and returns the number of backwards calls + * + * @param tokens - + * the attribute layout as a List of AttributeElements + */ + private void resolveCalls() { + int backwardsCalls = 0; + for (int i = 0; i < attributeLayoutElements.size(); i++) { + AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements.get(i); + if (element instanceof Callable) { + Callable callable = (Callable) element; + List body = callable.body; // Look for calls in the body + for (Iterator iter = body.iterator(); iter.hasNext();) { + LayoutElement layoutElement = (LayoutElement) iter.next(); + if (layoutElement instanceof Call) { + // Set the callable for each call + Call call = (Call) layoutElement; + int index = call.callableIndex; + if (index == 0) { // Calls the parent callable + backwardsCalls++; + call.setCallable(callable); + } else if (index > 0) { // Forwards call + for (int k = i; k < attributeLayoutElements.size(); k++) { + AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements + .get(k); + if (el instanceof Callable) { + index--; + if (index == 0) { + call.setCallable((Callable) el); + break; + } + } + } + } else { // Backwards call + backwardsCalls++; + for (int k = i; k >= 0; k--) { + AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements + .get(k); + if (el instanceof Callable) { + index++; + if (index == 0) { + call.setCallable((Callable) el); + break; + } + } + } + } + } + } + } + } + backwardsCallCount = backwardsCalls; + } + + private AttributeLayoutElement readNextAttributeElement(StringReader stream) + throws IOException { + int nextChar = stream.read(); + if (nextChar == -1) { + return null; + } + if (nextChar == '[') { + List body = readBody(getStreamUpToMatchingBracket(stream)); + return new Callable(body); + } else { + return readNextLayoutElement(stream); + } + } + + private LayoutElement readNextLayoutElement(StringReader stream) + throws IOException { + int nextChar = stream.read(); + if (nextChar == -1) { + return null; + } + switch (nextChar) { + // Integrals + case 'B': + case 'H': + case 'I': + case 'V': + return new Integral(new String(new char[] { (char) nextChar })); + case 'S': + case 'F': + return new Integral(new String(new char[] { (char) nextChar, + (char) stream.read() })); + case 'P': + stream.mark(1); + if (stream.read() != 'O') { + stream.reset(); + return new Integral("P" + (char) stream.read()); + } else { + return new Integral("PO" + (char) stream.read()); + } + case 'O': + stream.mark(1); + if (stream.read() != 'S') { + stream.reset(); + return new Integral("O" + (char) stream.read()); + } else { + return new Integral("OS" + (char) stream.read()); + } + + // Replication + case 'N': + char uint_type = (char) stream.read(); + stream.read(); // '[' + String str = readUpToMatchingBracket(stream); + return new Replication("" + uint_type, str); + + // Union + case 'T': + String int_type = "" + (char) stream.read(); + if (int_type.equals("S")) { + int_type += (char) stream.read(); + } + List unionCases = new ArrayList(); + UnionCase c; + while ((c = readNextUnionCase(stream)) != null) { + unionCases.add(c); + } + stream.read(); // '(' + stream.read(); // '(' + stream.read(); // '[' + List body = null; + stream.mark(1); + char next = (char) stream.read(); + if (next != ']') { + stream.reset(); + body = readBody(getStreamUpToMatchingBracket(stream)); + } + return new Union(int_type, unionCases, body); + + // Call + case '(': + int number = readNumber(stream); + stream.read(); // ')' + return new Call(number); + // Reference + case 'K': + case 'R': + String string = "" + nextChar + (char) stream.read(); + char nxt = (char) stream.read(); + string += nxt; + if (nxt == 'N') { + string += (char) stream.read(); + } + return new Reference(string); + } + return null; + } + + /** + * Read a UnionCase from the stream + * @param stream + * @return + * @throws IOException + */ + private UnionCase readNextUnionCase(StringReader stream) throws IOException { + stream.mark(2); + stream.read(); // '(' + char next = (char) stream.read(); + if (next == ')') { + stream.reset(); + return null; + } + List tags = new ArrayList(); + while (next != ')') { + tags.add(new Integer(readNumber(stream))); + next = (char) stream.read(); + } + stream.read(); // '[' + stream.mark(1); + next = (char) stream.read(); + if (next == ']') { + return new UnionCase(tags); + } else { + stream.reset(); + return new UnionCase(tags, + readBody(getStreamUpToMatchingBracket(stream))); + } + } + + /** + * An AttributeLayoutElement is a part of an attribute layout and has one or more + * bands associated with it, which transmit the AttributeElement data for + * successive Attributes of this type. + */ + private interface AttributeLayoutElement { + + /** + * Read the bands associated with this part of the layout + * + * @param in + * @param count + * @throws Pack200Exception + * @throws IOException + */ + public void readBands(InputStream in, int count) throws IOException, Pack200Exception; + + /** + * Add the band data for this element at the given index to the attribute + * + * @param index + * @param attribute + */ + public void addToAttribute(int index, NewAttribute attribute); + + } + + private abstract class LayoutElement implements AttributeLayoutElement { + + protected int getLength(char uint_type) { + int length = 0;; + switch(uint_type) { + case 'B': + length = 1; + break; + case 'H': + length = 2; + break; + case 'I': + length = 4; + break; + case 'V': + length = 0; + break; + } + return length; + } + } + + private class Integral extends LayoutElement { + + private String tag; + private long[] band; + + public Integral(String tag) { + this.tag = tag; + } + + public void readBands(InputStream in, int count) throws IOException, Pack200Exception { + band = decodeBandLong(attributeLayout.getName() + "_" + tag, in, (BHSDCodec) getCodec(tag), count); + } + + public void addToAttribute(int n, NewAttribute attribute) { + long value = band[n]; + if(tag.equals("B") || tag.equals("FB")) { + attribute.addInteger(1, value); + } else if (tag.equals("SB")) { + attribute.addInteger(1, (byte)value); + } else if(tag.equals("H") || tag.equals("FH")) { + attribute.addInteger(2, value); + } else if (tag.equals("SH")) { + attribute.addInteger(2, (short)value); + } else if (tag.equals("I") || tag.equals("FI")) { + attribute.addInteger(4, value); + } else if (tag.equals("SI")) { + attribute.addInteger(4, (int) value); + } else if (tag.equals("V") || tag.equals("FV") || tag.equals("SV")) { + // Don't add V's - they shouldn't be written out to the class file + } else if (tag.startsWith("PO")) { + char uint_type = tag.substring(2).toCharArray()[0]; + int length = getLength(uint_type); + attribute.addBCOffset(length, value); + } else if (tag.startsWith("P")) { + char uint_type = tag.substring(1).toCharArray()[0]; + int length = getLength(uint_type); + attribute.addBCIndex(length, value); + } else if (tag.startsWith("OS")) { + char uint_type = tag.substring(1).toCharArray()[0]; + int length = getLength(uint_type); + if(length == 1) { + value = (byte)value; + } else if(length == 2) { + value = (short)value; + } else if(length == 4) { + value = (int)value; + } + attribute.addBCLength(length, value); + } else if (tag.startsWith("O")) { + char uint_type = tag.substring(1).toCharArray()[0]; + int length = getLength(uint_type); + attribute.addBCLength(length, value); + } + } + + long getValue(int index) { + return band[index]; + } + + } + + /** + * A replication is an array of layout elements, with an associated count + */ + private class Replication extends LayoutElement { + + private Integral countElement; + + private List layoutElements = new ArrayList(); + + public Replication(String tag, String contents) throws IOException { + this.countElement = new Integral(tag); + StringReader stream = new StringReader(contents); + LayoutElement e; + while ((e = readNextLayoutElement(stream)) != null) { + layoutElements.add(e); + } + } + + public void readBands(InputStream in, int count) throws IOException, Pack200Exception { + countElement.readBands(in, count); + int arrayCount = 0; + for (int i = 0; i < count; i++) { + arrayCount += countElement.getValue(i); + } + for (Iterator iter = layoutElements.iterator(); iter.hasNext();) { + LayoutElement element = (LayoutElement) iter.next(); + element.readBands(in, arrayCount); + } + } + + public void addToAttribute(int index, NewAttribute attribute) { + // Add the count value + countElement.addToAttribute(index, attribute); + + // Add the corresponding array values + int offset = 0; + for (int i = 0; i < index; i++) { + offset += countElement.getValue(i); + } + long numElements = countElement.getValue(index); + for (int i = offset; i < offset + numElements; i++) { + for (Iterator iter = layoutElements.iterator(); iter.hasNext();) { + LayoutElement element = (LayoutElement) iter.next(); + element.addToAttribute(i, attribute); + } + } + } + } + + + /** + * A Union is a type of layout element where the tag value acts as a + * selector for one of the union cases + */ + private class Union extends LayoutElement { + + private Integral unionTag; + private List unionCases; + private List defaultCaseBody; + private int[] caseCounts; + private int defaultCount; + + public Union(String tag, List unionCases, List body) { + this.unionTag = new Integral(tag); + this.unionCases = unionCases; + this.defaultCaseBody = body; + } + + public void readBands(InputStream in, int count) throws IOException, Pack200Exception { + unionTag.readBands(in, count); + long[] values = unionTag.band; + // Count the band size for each union case then read the bands + caseCounts = new int[unionCases.size()]; + for (int i = 0; i < caseCounts.length; i++) { + UnionCase unionCase = (UnionCase) unionCases.get(i); + for (int j = 0; j < values.length; j++) { + if(unionCase.hasTag(values[j])) { + caseCounts[i]++; + } + } + unionCase.readBands(in, caseCounts[i]); + } + // Count number of default cases then read the default bands + for (int i = 0; i < values.length; i++) { + boolean found = false; + for (Iterator iter = unionCases.iterator(); iter.hasNext();) { + UnionCase unionCase = (UnionCase) iter.next(); + if(unionCase.hasTag(values[i])) { + found = true; + } + } + if(!found) { + defaultCount++; + } + } + if(defaultCaseBody != null) { + for (Iterator iter = defaultCaseBody.iterator(); iter.hasNext();) { + LayoutElement element = (LayoutElement) iter.next(); + element.readBands(in, defaultCount); + } + } + } + + public void addToAttribute(int n, NewAttribute attribute) { + unionTag.addToAttribute(n, attribute); + int offset = 0; + long[] tagBand = unionTag.band; + long tag = unionTag.getValue(n); + boolean defaultCase = true; + for (Iterator iter = unionCases.iterator(); iter.hasNext();) { + UnionCase element = (UnionCase) iter.next(); + if(element.hasTag(tag)) { + defaultCase = false; + for (int j = 0; j < n; j++) { + if(element.hasTag(tagBand[j])) { + offset++; + } + } + element.addToAttribute(offset, attribute); + } + } + if(defaultCase) { + // default case + int defaultOffset = 0; + for (int j = 0; j < n; j++) { + boolean found = false; + for (Iterator iter = unionCases.iterator(); iter.hasNext();) { + UnionCase element = (UnionCase) iter.next(); + if(element.hasTag(tagBand[j])) { + found = true; + } + } + if(!found) { + defaultOffset++; + } + } + if(defaultCaseBody != null) { + for (Iterator iter = defaultCaseBody.iterator(); iter.hasNext();) { + LayoutElement element = (LayoutElement) iter.next(); + element.addToAttribute(defaultOffset, attribute); + } + } + } + } + + } + + private class Call extends LayoutElement { + + private int callableIndex; + private Callable callable; + + public Call(int callableIndex) { + this.callableIndex = callableIndex; + } + + public void setCallable(Callable callable) { + this.callable = callable; + if(callableIndex < 1) { + callable.setBackwardsCallable(); + } + } + + public void readBands(InputStream in, int count) { + /* + * We don't read anything here, but we need to pass the extra count + * to the callable if it's a forwards call. For backwards callables + * the count is transmitted directly in the attribute bands and + * so it is added later. + */ + if(callableIndex > 0) { + callable.addCount(count); + } + } + + public void addToAttribute(int n, NewAttribute attribute) { + callable.addNextToAttribute(attribute); + } + } + + /** + * Constant Pool Reference + */ + private class Reference extends LayoutElement { + + private String tag; + + private Object band; + + private int length; + + public Reference(String tag) { + this.tag = tag; + length = getLength(tag.charAt(tag.length())); + } + + public void readBands(InputStream in, int count) throws IOException, Pack200Exception { + if(tag.startsWith("KI")) { // Integer + band = parseCPIntReferences(attributeLayout.getName(), in, Codec.UNSIGNED5, count); + } else if(tag.startsWith("KJ")) { // Long + band = parseCPLongReferences(attributeLayout.getName(), in, Codec.UNSIGNED5, count); + } else if(tag.startsWith("KF")) { // Float + band = parseCPFloatReferences(attributeLayout.getName(), in, Codec.UNSIGNED5, count); + } else if(tag.startsWith("KD")) { // Double + band = parseCPDoubleReferences(attributeLayout.getName(), in, Codec.UNSIGNED5, count); + } else if(tag.startsWith("KS")) { // String + band = parseCPStringReferences(attributeLayout.getName(), in, Codec.UNSIGNED5, count); + } else if(tag.startsWith("RC")) { // Class + band = parseCPClassReferences(attributeLayout.getName(), in, Codec.UNSIGNED5, count); + } else if(tag.startsWith("RS")) { // Signature + band = parseCPSignatureReferences(attributeLayout.getName(), in, Codec.UNSIGNED5, count); + } else if(tag.startsWith("RD")) { // Descriptor + band = parseCPDescriptorReferences(attributeLayout.getName(), in, Codec.UNSIGNED5, count); + } else if(tag.startsWith("RF")) { // Field Reference + band = parseCPFieldRefReferences(attributeLayout.getName(), in, Codec.UNSIGNED5, count); + } else if(tag.startsWith("RM")) { // Method Reference + band = parseCPMethodRefReferences(attributeLayout.getName(), in, Codec.UNSIGNED5, count); + } else if(tag.startsWith("RI")) { // Interface Method Reference + band = parseCPInterfaceMethodRefReferences(attributeLayout.getName(), in, Codec.UNSIGNED5, count); + } else if(tag.startsWith("RU")) { // UTF8 String + band = parseCPUTF8References(attributeLayout.getName(), in, Codec.UNSIGNED5, count); + } + } + + public void addToAttribute(int n, NewAttribute attribute) { + if(tag.startsWith("KI")) { // Integer + attribute.addCPConstant(length, ((CPInteger[])band)[n]); + } else if(tag.startsWith("KJ")) { // Long + attribute.addCPConstant(length, ((CPLong[])band)[n]); + } else if(tag.startsWith("KF")) { // Float + attribute.addCPConstant(length, ((CPFloat[])band)[n]); + } else if(tag.startsWith("KD")) { // Double + attribute.addCPConstant(length, ((CPDouble[])band)[n]); + } else if(tag.startsWith("KS")) { // String + attribute.addCPConstant(length, ((CPString[])band)[n]); + } else if(tag.startsWith("RC")) { // Class + attribute.addCPClass(length, ((CPClass[])band)[n]); + } else if(tag.startsWith("RS")) { // Signature + attribute.addCPUTF8(length, ((CPUTF8[])band)[n]); + } else if(tag.startsWith("RD")) { // Descriptor + attribute.addCPNameAndType(length, ((CPNameAndType[])band)[n]); + } else if(tag.startsWith("RF")) { // Field Reference + attribute.addCPFieldRef(length, ((CPFieldRef[])band)[n]); + } else if(tag.startsWith("RM")) { // Method Reference + attribute.addCPMethodRef(length, ((CPMethodRef[])band)[n]); + } else if(tag.startsWith("RI")) { // Interface Method Reference + attribute.addCPIMethodRef(length, ((CPInterfaceMethodRef[])band)[n]); + } else if(tag.startsWith("RU")) { // UTF8 String + attribute.addCPUTF8(length, ((CPUTF8[])band)[n]); + } + } + + } + + private class Callable implements AttributeLayoutElement { + + private List body; + + private boolean isBackwardsCallable; + + public Callable(List body) throws IOException { + this.body = body; + } + + private int count; + private int index; + + /** + * Used by calls when adding band contents to attributes + * so they don't have to keep track of the internal index + * of the callable + * @param attribute + */ + public void addNextToAttribute(NewAttribute attribute) { + for (Iterator iter = body.iterator(); iter.hasNext();) { + LayoutElement element = (LayoutElement) iter.next(); + element.addToAttribute(index, attribute); + } + index++; + } + + /** + * Adds the count of a call to this callable (ie the number of calls) + * @param count + */ + public void addCount(int count) { + this.count += count; + } + + public void readBands(InputStream in, int count) throws IOException, Pack200Exception { + count += this.count; + for (Iterator iter = body.iterator(); iter.hasNext();) { + LayoutElement element = (LayoutElement) iter.next(); + element.readBands(in, count); + } + } + + public void addToAttribute(int n, NewAttribute attribute) { + // Ignore n because bands also contain element parts from calls + for (Iterator iter = body.iterator(); iter.hasNext();) { + LayoutElement element = (LayoutElement) iter.next(); + element.addToAttribute(index, attribute); + } + index++; + } + + public boolean isBackwardsCallable() { + return isBackwardsCallable; + } + + /** + * Tells this Callable that it is a backwards callable + */ + public void setBackwardsCallable() { + this.isBackwardsCallable = true; + } + } + + /** + * A Union case + */ + private class UnionCase extends LayoutElement { + + private List body; + + private List tags; + + public UnionCase(List tags) { + this.tags = tags; + } + + public boolean hasTag(long l) { + return tags.contains(new Integer((int) l)); + } + + public UnionCase(List tags, List body) throws IOException { + this.tags = tags; + this.body = body; + } + + public void readBands(InputStream in, int count) throws IOException, Pack200Exception { + if(body != null) { + for (Iterator iter = body.iterator(); iter.hasNext();) { + LayoutElement element = (LayoutElement) iter.next(); + element.readBands(in, count); + } + } + } + + public void addToAttribute(int index, NewAttribute attribute) { + if(body != null) { + for (Iterator iter = body.iterator(); iter.hasNext();) { + LayoutElement element = (LayoutElement) iter.next(); + element.addToAttribute(index, attribute); + } + } + } + } + + /** + * Utility method to get the contents of the given stream, up to the next ']', + * (ignoring pairs of brackets '[' and ']') + * @param stream + * @return + * @throws IOException + */ + private StringReader getStreamUpToMatchingBracket(StringReader stream) + throws IOException { + StringBuffer sb = new StringBuffer(); + int foundBracket = -1; + while (foundBracket != 0) { + char c = (char) stream.read(); + if (c == ']') { + foundBracket++; + } + if (c == '[') { + foundBracket--; + } + if (!(foundBracket == 0)) { + sb.append(c); + } + } + return new StringReader(sb.toString()); + } + + /** + * Returns the codec that should be used for the given layout element + * @param layoutElement + * @return + */ + public BHSDCodec getCodec(String layoutElement) { + if (layoutElement.indexOf("O") >= 0) { //$NON-NLS-1$ + return Codec.BRANCH5; + } else if (layoutElement.indexOf("P") >= 0) { //$NON-NLS-1$ + return Codec.BCI5; + } else if (layoutElement.indexOf("S") >= 0 && layoutElement.indexOf("KS") < 0 //$NON-NLS-1$ //$NON-NLS-2$ + && layoutElement.indexOf("RS") < 0) { //$NON-NLS-1$ + return Codec.SIGNED5; + } else if (layoutElement.indexOf("B") >= 0) { //$NON-NLS-1$ + return Codec.BYTE1; + } else { + return Codec.UNSIGNED5; + } + } + + /** + * Utility method to get the contents of the given stream, up to the next ']', + * (ignoring pairs of brackets '[' and ']') + * @param stream + * @return + * @throws IOException + */ + private String readUpToMatchingBracket(StringReader stream) + throws IOException { + StringBuffer sb = new StringBuffer(); + int foundBracket = -1; + while (foundBracket != 0) { + char c = (char) stream.read(); + if (c == ']') { + foundBracket++; + } + if (c == '[') { + foundBracket--; + } + if (!(foundBracket == 0)) { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * Read a number from the stream and return it + * @param stream + * @return + * @throws IOException + */ + private int readNumber(StringReader stream) throws IOException { + stream.mark(1); + char first = (char) stream.read(); + boolean negative = first == '-'; + if (!negative) { + stream.reset(); + } + stream.mark(100); + int i; + int length = 0; + while ((i = (stream.read())) != -1 && Character.isDigit((char) i)) { + length++; + } + stream.reset(); + char[] digits = new char[length]; + stream.read(digits); + return Integer.parseInt((negative ? "-" : "") + new String(digits)); + } + + /** + * Read a 'body' section of the layout from the given stream + * @param stream + * @return List of LayoutElements + * @throws IOException + */ + private List readBody(StringReader stream) throws IOException { + List layoutElements = new ArrayList(); + LayoutElement e; + while ((e = readNextLayoutElement(stream)) != null) { + layoutElements.add(e); + } + return layoutElements; + } + + public int getBackwardsCallCount() { + return backwardsCallCount; + } + + /** + * Once the attribute bands have been read the callables can be informed + * about the number of times each is subject to a backwards call. This + * method is used to set this information. + * + * @param backwardsCalls + * one int for each backwards callable, which contains the number + * of times that callable is subject to a backwards call. + * @throws IOException + */ + public void setBackwardsCalls(int[] backwardsCalls) throws IOException { + int index = 0; + parseLayout(); + for (Iterator iter = attributeLayoutElements.iterator(); iter.hasNext();) { + AttributeLayoutElement element = (AttributeLayoutElement) iter.next(); + if(element instanceof Callable && ((Callable)element).isBackwardsCallable()) { + ((Callable)element).addCount(backwardsCalls[index]); + index++; + } + } + } + +} \ No newline at end of file Index: src/test/java/org/apache/harmony/pack200/tests/BandSetTest.java =================================================================== --- src/test/java/org/apache/harmony/pack200/tests/BandSetTest.java (revision 587112) +++ src/test/java/org/apache/harmony/pack200/tests/BandSetTest.java (working copy) @@ -26,7 +26,6 @@ import org.apache.harmony.pack200.BHSDCodec; import org.apache.harmony.pack200.BandSet; import org.apache.harmony.pack200.Codec; -import org.apache.harmony.pack200.CodecEncoding; import org.apache.harmony.pack200.Pack200Exception; import org.apache.harmony.pack200.Segment; import org.apache.harmony.pack200.SegmentHeader;