Index: main/java/org/apache/harmony/pack200/SegmentConstantPool.java =================================================================== --- main/java/org/apache/harmony/pack200/SegmentConstantPool.java (revision 600720) +++ main/java/org/apache/harmony/pack200/SegmentConstantPool.java (working copy) @@ -17,7 +17,6 @@ package org.apache.harmony.pack200; import java.util.ArrayList; -import java.util.Arrays; import org.apache.harmony.pack200.bytecode.CPClass; import org.apache.harmony.pack200.bytecode.CPDouble; @@ -29,7 +28,6 @@ import org.apache.harmony.pack200.bytecode.CPMethodRef; import org.apache.harmony.pack200.bytecode.CPString; import org.apache.harmony.pack200.bytecode.CPUTF8; -import org.apache.harmony.pack200.bytecode.ClassFileEntry; import org.apache.harmony.pack200.bytecode.ConstantPoolEntry; public class SegmentConstantPool { Index: main/java/org/apache/harmony/pack200/ClassBands.java =================================================================== --- main/java/org/apache/harmony/pack200/ClassBands.java (revision 600720) +++ main/java/org/apache/harmony/pack200/ClassBands.java (working copy) @@ -1000,30 +1000,26 @@ } /** - * Answer an ArrayList of the LineNumberTables corresponding to all classes. - * If a class doesn't have a LineNumberTable, the corresponding element in this - * list will be null. + * Answer an ArrayList of ArrayLists which hold the the code attributes + * corresponding to all classes in order. + * + * If a class doesn't have any attributes, the corresponding element in this + * list will be an empty ArrayList. * @return ArrayList */ - // TODO: the class file spec allows >1 LineNumberTable per method. Does Pack200 spec fold them all into one? (If not, need to handle that case.) - public ArrayList getLineNumberAttributes() { - ArrayList lineNumberList = new ArrayList(); - for(int classIndex=0; classIndex < codeAttributes.length; classIndex++) { - boolean foundLineNumberTable = false; - for(int attributeIndex = 0; attributeIndex < codeAttributes[classIndex].size(); attributeIndex++) { - Attribute attribute = (Attribute)codeAttributes[classIndex].get(attributeIndex); - if(attribute.getClass() == LineNumberTableAttribute.class) { - foundLineNumberTable = true; - lineNumberList.add(attribute); - } - } - if(!foundLineNumberTable) { - lineNumberList.add(null); - } - } - return lineNumberList; + public ArrayList getOrderedCodeAttributes() { + ArrayList orderedAttributeList = new ArrayList(); + for(int classIndex=0; classIndex < codeAttributes.length; classIndex++) { + ArrayList currentAttributes = new ArrayList(); + for(int attributeIndex = 0; attributeIndex < codeAttributes[classIndex].size(); attributeIndex++) { + Attribute attribute = (Attribute)codeAttributes[classIndex].get(attributeIndex); + currentAttributes.add(attribute); + } + orderedAttributeList.add(currentAttributes); + } + return orderedAttributeList; } - + public ArrayList[][] getMethodAttributes() { return methodAttributes; } Index: main/java/org/apache/harmony/pack200/bytecode/ClassConstantPool.java =================================================================== --- main/java/org/apache/harmony/pack200/bytecode/ClassConstantPool.java (revision 600720) +++ main/java/org/apache/harmony/pack200/bytecode/ClassConstantPool.java (working copy) @@ -62,7 +62,12 @@ public int indexOf(ClassFileEntry entry) { if (!resolved) throw new IllegalStateException("Constant pool is not yet resolved; this does not make any sense"); - return entries.indexOf(entry) + 1; + int entryIndex = entries.indexOf(entry); + // If the entry isn't found, answer -1. Otherwise answer the entry. + if(entryIndex != -1) { + return entryIndex + 1; + } + return -1; } public int size() { Index: main/java/org/apache/harmony/pack200/bytecode/BCIRenumberedAttribute.java =================================================================== --- main/java/org/apache/harmony/pack200/bytecode/BCIRenumberedAttribute.java (revision 0) +++ main/java/org/apache/harmony/pack200/bytecode/BCIRenumberedAttribute.java (revision 0) @@ -0,0 +1,68 @@ +/* + * 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; +import java.util.List; + +import org.apache.harmony.pack200.SegmentUtils; + +public abstract class BCIRenumberedAttribute extends Attribute { + + protected boolean renumbered = false; + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.bytecode.Attribute#hasBCIRenumbering() + */ + public boolean hasBCIRenumbering() { + return true; + } + + public BCIRenumberedAttribute(String attributeName) { + super(attributeName); + } + + protected abstract int getLength(); + protected abstract void writeBody(DataOutputStream dos) throws IOException; + public abstract String toString(); + protected abstract int[] getStartPCs(); + + /** + * In Pack200, line number tables are BCI renumbered. + * This method takes the byteCodeOffsets (which is + * a List of Integers specifying the offset in the + * byte code array of each instruction) and updates the + * start_pcs so that it points to the instruction index + * itself, not the BCI renumbering of the instruction. + * + * @param byteCodeOffsets List of Integer offsets of the bytecode array + */ + public void renumber(List byteCodeOffsets) { + if(renumbered) { + SegmentUtils.debug("Trying to renumber something renumbered"); + return; +// throw new Error("Trying to renumber a line number table that has already been renumbered"); + } + renumbered = true; + int[] startPCs = getStartPCs(); + for(int index=0; index < startPCs.length; index++) { + startPCs[index] = ((Integer)byteCodeOffsets.get(startPCs[index])).intValue(); + } + } + +} Index: main/java/org/apache/harmony/pack200/bytecode/LineNumberTableAttribute.java =================================================================== --- main/java/org/apache/harmony/pack200/bytecode/LineNumberTableAttribute.java (revision 600720) +++ main/java/org/apache/harmony/pack200/bytecode/LineNumberTableAttribute.java (working copy) @@ -18,17 +18,13 @@ import java.io.DataOutputStream; import java.io.IOException; -import java.util.Iterator; import java.util.List; -import org.apache.harmony.pack200.SegmentUtils; +public class LineNumberTableAttribute extends BCIRenumberedAttribute { -public class LineNumberTableAttribute extends Attribute { - private int line_number_table_length; private int[] start_pcs; private int[] line_numbers; - private boolean renumbered = false; public LineNumberTableAttribute(int line_number_table_length, int[] start_pcs, int[] line_numbers) { super("LineNumberTable"); @@ -64,23 +60,7 @@ super.resolve(pool); } - /** - * In Pack200, line number tables are BCI renumbered. - * This method takes the byteCodeOffsets (which is - * a List of Integers specifying the offset in the - * byte code array of each instruction) and updates the - * start_pcs so that it points to the instruction index - * itself, not the BCI renumbering of the instruction. - * - * @param byteCodeOffsets List of Integer offsets of the bytecode array - */ - public void renumberLineNumberTable(List byteCodeOffsets) { - if(renumbered) { - throw new Error("Trying to renumber a line number table that has already been renumbered"); - } - renumbered = true; - for(int index=0; index < line_numbers.length; index++) { - start_pcs[index] = ((Integer)byteCodeOffsets.get(start_pcs[index])).intValue(); - } - } + protected int[] getStartPCs() { + return start_pcs; + } } Index: main/java/org/apache/harmony/pack200/bytecode/LocalVariableTableAttribute.java =================================================================== --- main/java/org/apache/harmony/pack200/bytecode/LocalVariableTableAttribute.java (revision 600720) +++ main/java/org/apache/harmony/pack200/bytecode/LocalVariableTableAttribute.java (working copy) @@ -18,10 +18,10 @@ import java.io.DataOutputStream; import java.io.IOException; +import java.util.List; -public class LocalVariableTableAttribute extends Attribute { +public class LocalVariableTableAttribute extends BCIRenumberedAttribute { - private int local_variable_table_length; private int[] start_pcs; private int[] lengths; @@ -60,9 +60,15 @@ protected void resolve(ClassConstantPool pool) { super.resolve(pool); + pool.add(getAttributeName()); name_indexes = new int[local_variable_table_length]; descriptor_indexes = new int[local_variable_table_length]; for (int i = 0; i < local_variable_table_length; i++) { + // TODO: is this the right place to add the names and descriptors? + // Maybe some API to say where they should be added if they're not + // already in the cp? + pool.add(names[i]); + pool.add(descriptors[i]); names[i].resolve(pool); descriptors[i].resolve(pool); name_indexes[i] = pool.indexOf(names[i]); @@ -71,7 +77,35 @@ } public String toString() { - return "LocalVariableTable: " + + local_variable_table_length + " varaibles"; + return "LocalVariableTable: " + + local_variable_table_length + " variables"; } + protected int[] getStartPCs() { + return start_pcs; + } + + /* (non-Javadoc) + * @see org.apache.harmony.pack200.bytecode.BCIRenumberedAttribute#renumber(java.util.List) + */ + public void renumber(List byteCodeOffsets) { + // First fix up the start_pcs + super.renumber(byteCodeOffsets); + // Next fix up the lengths + int maxLength = ((Integer)byteCodeOffsets.get(byteCodeOffsets.size() - 1)).intValue(); + for(int index=0; index < lengths.length; index++) { + // Need to special case when the length is greater than the size + int revisedLength = -1; + int encodedLength = lengths[index]; + // Length can either be an index into the byte code offsets, or one beyond the + // end of the byte code offsets. Need to determine which this is. + if(encodedLength == byteCodeOffsets.size()) { + // Pointing to one past the end of the byte code array + revisedLength = maxLength - start_pcs[index] + 1; + } else { + // We're indexed into the byte code array + revisedLength = ((Integer)byteCodeOffsets.get(encodedLength)).intValue(); + } + lengths[index] = revisedLength; + } + } } Index: main/java/org/apache/harmony/pack200/bytecode/ByteCode.java =================================================================== --- main/java/org/apache/harmony/pack200/bytecode/ByteCode.java (revision 600720) +++ main/java/org/apache/harmony/pack200/bytecode/ByteCode.java (working copy) @@ -35,6 +35,9 @@ private int[][] nestedPositions; private int[] rewrite; + private int byteCodeOffset = -1; + private int byteCodeTarget = -1; + protected ByteCode(int opcode) { this(opcode, ClassFileEntry.NONE); } @@ -193,11 +196,26 @@ throw new Error("Trying to rewrite " + this + " with an int at position " + position + " but this won't fit in the rewrite array"); } - rewrite[firstOperandIndex + position] = (operand & 0xFF00) >> 8; - rewrite[firstOperandIndex + position + 1] = operand & 0xFF; + rewrite[firstOperandIndex + position] = (operand & 0xFF00) >> 8; + rewrite[firstOperandIndex + position + 1] = operand & 0xFF; } /** + * This is just like setOperandInt, but takes special care when the + * operand is less than 0 to make sure it's written correctly. + * @param operand int to set the rewrite bytes to + * @param position int position of the operands in the rewrite bytes + */ + public void setOperandSignedInt(int operand, int position) { + if(operand >= 0) { + setOperandInt(operand, position); + } else { + int twosComplementOperand = 0x10000 + operand; + setOperandInt(twosComplementOperand, position); + } + } + + /** * Given an int operand, treat it as a byte and set * the rewrite byte for that position to that value. * Mask of anything beyond 0xFF. @@ -274,4 +292,54 @@ public boolean hasMultipleByteCodes() { return getByteCodeForm().hasMultipleByteCodes(); } + + /** + * ByteCodes may need to know their position in the + * code array (in particular, label byte codes need + * to know where they are in order to calculate their + * targets). This method lets the CodeAttribute specify + * where the byte code is. + * + * Since there are no aload0+label instructions, this + * method doesn't worry about multioperation bytecodes. + * + * @param byteCodeOffset int position in code array. + */ + public void setByteCodeIndex(int byteCodeOffset) { + this.byteCodeOffset = byteCodeOffset; + } + + + public int getByteCodeIndex() { + return byteCodeOffset; + } + + /** + * Some ByteCodes (in particular, LabelForm bytecodes) + * have to keep track of a byteCodeTarget. This is + * initially an offset in the CodeAttribute array + * relative to the byteCodeOffset, but later gets fixed + * up to point to the absolute position in the CodeAttribute + * array. This method sets the target. + * + * @param byteCodeTarget int index in array + */ + public void setByteCodeTarget(int byteCodeTarget) { + this.byteCodeTarget = byteCodeTarget; + } + + public int getByteCodeTarget() { + return byteCodeTarget; + } + + /** + * Some ByteCodes (in particular, those with the Label + * form) need to be fixed up after all the bytecodes + * in the CodeAttribute have been added. (This can't + * be done beforehand because the CodeAttribute needs + * to be complete before targets can be assigned.) + */ + public void applyByteCodeTargetFixup(CodeAttribute codeAttribute) { + getByteCodeForm().fixUpByteCodeTarget(this, codeAttribute); + } } Index: main/java/org/apache/harmony/pack200/bytecode/CodeAttribute.java =================================================================== --- main/java/org/apache/harmony/pack200/bytecode/CodeAttribute.java (revision 600720) +++ main/java/org/apache/harmony/pack200/bytecode/CodeAttribute.java (working copy) @@ -43,9 +43,12 @@ byteCodeOffsets.add(new Integer(0)); for (int i = 0; i < codePacked.length; i++) { ByteCode byteCode = ByteCode.getByteCode(codePacked[i] & 0xff); + // Setting the offset must happen before extracting operands + // because label bytecodes need to know their offsets. + byteCode.setByteCodeIndex(i); byteCode.extractOperands(operandManager, segment); byteCodes.add(byteCode); - this.codeLength += byteCode.getLength(); + codeLength += byteCode.getLength(); int lastBytecodePosition = ((Integer) byteCodeOffsets .get(byteCodeOffsets.size() - 1)).intValue(); // This code assumes all multiple byte bytecodes are @@ -62,6 +65,12 @@ + byteCode.getLength())); } } + // Now that all the bytecodes know their positions and + // sizes, fix up the byte code targets + for (int i = 0; i < codePacked.length; i++) { + ByteCode byteCode = (ByteCode)byteCodes.get(i); + byteCode.applyByteCodeTargetFixup(this); + } } protected int getLength() { Index: main/java/org/apache/harmony/pack200/bytecode/ClassFile.java =================================================================== --- main/java/org/apache/harmony/pack200/bytecode/ClassFile.java (revision 600720) +++ main/java/org/apache/harmony/pack200/bytecode/ClassFile.java (working copy) @@ -56,7 +56,7 @@ fields[i].write(dos); } dos.writeShort(methods.length); - for(int i=0;i