001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.compress.harmony.unpack200.bytecode; 018 019import java.io.DataOutputStream; 020import java.io.IOException; 021 022import org.apache.commons.compress.harmony.unpack200.Segment; 023import org.apache.commons.compress.harmony.unpack200.bytecode.forms.ByteCodeForm; 024 025/** 026 * A bytecode class file entry. 027 */ 028public class ByteCode extends ClassFileEntry { 029 030 private static ByteCode[] noArgByteCodes = new ByteCode[255]; 031 032 public static ByteCode getByteCode(final int opcode) { 033 final int byteOpcode = 0xFF & opcode; 034 if (ByteCodeForm.get(byteOpcode).hasNoOperand()) { 035 if (null == noArgByteCodes[byteOpcode]) { 036 noArgByteCodes[byteOpcode] = new ByteCode(byteOpcode); 037 } 038 return noArgByteCodes[byteOpcode]; 039 } 040 return new ByteCode(byteOpcode); 041 } 042 043 private final ByteCodeForm byteCodeForm; 044 045 private ClassFileEntry[] nested; 046 private int[][] nestedPositions; 047 private int[] rewrite; 048 049 private int byteCodeOffset = -1; 050 private int[] byteCodeTargets; 051 052 protected ByteCode(final int opcode) { 053 this(opcode, NONE); 054 } 055 056 protected ByteCode(final int opcode, final ClassFileEntry[] nested) { 057 this.byteCodeForm = ByteCodeForm.get(opcode); 058 this.rewrite = byteCodeForm.getRewriteCopy(); 059 this.nested = nested; 060 } 061 062 /** 063 * Some ByteCodes (in particular, those with labels need to be fixed up after all the bytecodes in the CodeAttribute have been added. (This can't be done 064 * beforehand because the CodeAttribute needs to be complete before targets can be assigned.) 065 * 066 * @param codeAttribute the code attribute 067 */ 068 public void applyByteCodeTargetFixup(final CodeAttribute codeAttribute) { 069 getByteCodeForm().fixUpByteCodeTargets(this, codeAttribute); 070 } 071 072 @Override 073 protected void doWrite(final DataOutputStream dos) throws IOException { 074 for (final int element : rewrite) { 075 dos.writeByte(element); 076 } 077 } 078 079 @Override 080 public boolean equals(final Object obj) { 081 return this == obj; 082 } 083 084 public void extractOperands(final OperandManager operandManager, final Segment segment, final int codeLength) { 085 // Given an OperandTable, figure out which operands 086 // the receiver needs and stuff them in operands. 087 // Later on the operands can be rewritten (But that's 088 // later, not now). 089 final ByteCodeForm currentByteCodeForm = getByteCodeForm(); 090 currentByteCodeForm.setByteCodeOperands(this, operandManager, codeLength); 091 } 092 093 protected ByteCodeForm getByteCodeForm() { 094 return byteCodeForm; 095 } 096 097 public int getByteCodeIndex() { 098 return byteCodeOffset; 099 } 100 101 public int[] getByteCodeTargets() { 102 return byteCodeTargets; 103 } 104 105 public int getLength() { 106 return rewrite.length; 107 } 108 109 public String getName() { 110 return getByteCodeForm().getName(); 111 } 112 113 @Override 114 public ClassFileEntry[] getNestedClassFileEntries() { 115 return nested; 116 } 117 118 public int[] getNestedPosition(final int index) { 119 return getNestedPositions()[index]; 120 } 121 122 public int[][] getNestedPositions() { 123 return nestedPositions; 124 } 125 126 public int getOpcode() { 127 return getByteCodeForm().getOpcode(); 128 } 129 130 /** 131 * Some bytecodes (the ones with variable lengths) can't have a static rewrite array - they need the ability to update the array. This method permits their 132 * associated bytecode formst to query their rewrite array. 133 * 134 * Note that this should not be called from bytecodes which have a static rewrite; use the table in ByteCodeForm instead to specify those rewrites. 135 * 136 * @return Some bytecodes. 137 */ 138 public int[] getRewrite() { 139 return rewrite; 140 } 141 142 @Override 143 public int hashCode() { 144 return objectHashCode(); 145 } 146 147 /** 148 * This method will answer true if the receiver is a multi-bytecode instruction (such as aload0_putfield_super); otherwise, it will answer false. 149 * 150 * @return boolean true if multibytecode, false otherwise 151 */ 152 public boolean hasMultipleByteCodes() { 153 return getByteCodeForm().hasMultipleByteCodes(); 154 } 155 156 public boolean nestedMustStartClassPool() { 157 return byteCodeForm.nestedMustStartClassPool(); 158 } 159 160 /* 161 * (non-Javadoc) 162 * 163 * @see org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry#resolve(org.apache.commons.compress.harmony 164 * .unpack200.bytecode.ClassConstantPool) 165 */ 166 @Override 167 protected void resolve(final ClassConstantPool pool) { 168 super.resolve(pool); 169 if (nested.length > 0) { 170 // Update the bytecode rewrite array so that it points 171 // to the elements of the nested array. 172 for (int index = 0; index < nested.length; index++) { 173 final int argLength = getNestedPosition(index)[1]; 174 switch (argLength) { 175 176 case 1: 177 setOperandByte(pool.indexOf(nested[index]), getNestedPosition(index)[0]); 178 break; 179 180 case 2: 181 setOperand2Bytes(pool.indexOf(nested[index]), getNestedPosition(index)[0]); 182 break; 183 184 default: 185 throw new Error("Unhandled resolve " + this); 186 } 187 } 188 } 189 } 190 191 /** 192 * 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 193 * targets). This method lets the CodeAttribute specify where the byte code is. 194 * 195 * Since there are no aload0+label instructions, this method doesn't worry about multioperation bytecodes. 196 * 197 * @param byteCodeOffset int position in code array. 198 */ 199 public void setByteCodeIndex(final int byteCodeOffset) { 200 this.byteCodeOffset = byteCodeOffset; 201 } 202 203 /** 204 * Some ByteCodes (in particular, those with labels) have to keep track of byteCodeTargets. These are initially offsets in the CodeAttribute array relative 205 * to the byteCodeOffset, but later get fixed up to point to the absolute position in the CodeAttribute array. This method sets the targets. 206 * 207 * @param byteCodeTargets int index in array 208 */ 209 public void setByteCodeTargets(final int[] byteCodeTargets) { 210 this.byteCodeTargets = byteCodeTargets; 211 } 212 213 public void setNested(final ClassFileEntry[] nested) { 214 this.nested = nested; 215 } 216 217 /** 218 * nestedPositions is an array of arrays of ints. Each subarray specifies a position of a nested element (from the nested[] array) and the length of that 219 * element. 220 * 221 * For instance, one might have a nested of: {CPClass java/lang/Foo, CPFloat 3.14} The nestedPositions would then be: {{0,2},{2,2}} In other words, when the 222 * bytecode is resolved, the CPClass will be resolved to an int and inserted at position 0 and 1 of the rewrite arguments (the first occurrences of -1). The 223 * CPFloat will be resolved to an int position and inserted at positions 2 and 3 of the rewrite arguments. 224 * 225 * @param nestedPositions Each subarray specifies a position of a nested element (from the nested[] array) and the length of that element. 226 */ 227 public void setNestedPositions(final int[][] nestedPositions) { 228 this.nestedPositions = nestedPositions; 229 } 230 231 /** 232 * Given an int operand, set the rewrite bytes for that position and the one immediately following it to a high-byte, low-byte encoding of the operand. 233 * 234 * @param operand int to set the rewrite bytes to 235 * @param position int position in the operands of the rewrite bytes. For a rewrite array of {100, -1, -1, -1} position 0 is the first -1, position 1 is the 236 * second -1, etc. 237 */ 238 public void setOperand2Bytes(final int operand, final int position) { 239 final int firstOperandIndex = getByteCodeForm().firstOperandIndex(); 240 final int byteCodeFormLength = getByteCodeForm().getRewrite().length; 241 if (firstOperandIndex < 1) { 242 // No operand rewriting permitted for this bytecode 243 throw new Error("Trying to rewrite " + this + " that has no rewrite"); 244 } 245 246 if (firstOperandIndex + position + 1 > byteCodeFormLength) { 247 throw new Error("Trying to rewrite " + this + " with an int at position " + position + " but this won't fit in the rewrite array"); 248 } 249 250 rewrite[firstOperandIndex + position] = (operand & 0xFF00) >> 8; 251 rewrite[firstOperandIndex + position + 1] = operand & 0xFF; 252 } 253 254 /** 255 * 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. 256 * 257 * @param operand int to set the rewrite byte to (unsigned) 258 * @param position int position in the operands of the rewrite bytes. For a rewrite array of {100, -1, -1, -1} position 0 is the first -1, position 1 is the 259 * second -1, etc. 260 */ 261 public void setOperandByte(final int operand, final int position) { 262 final int firstOperandIndex = getByteCodeForm().firstOperandIndex(); 263 final int byteCodeFormLength = getByteCodeForm().operandLength(); 264 if (firstOperandIndex < 1) { 265 // No operand rewriting permitted for this bytecode 266 throw new Error("Trying to rewrite " + this + " that has no rewrite"); 267 } 268 269 if (firstOperandIndex + position > byteCodeFormLength) { 270 throw new Error("Trying to rewrite " + this + " with an byte at position " + position + " but this won't fit in the rewrite array"); 271 } 272 273 rewrite[firstOperandIndex + position] = operand & 0xFF; 274 } 275 276 /** 277 * Given an array of ints which correspond to bytes in the operand of the bytecode, set the rewrite bytes of the operand to be the appropriate values. All 278 * values in operands[] will be masked with 0xFF so they fit into a byte. 279 * 280 * @param operands int[] rewrite operand bytes 281 */ 282 public void setOperandBytes(final int[] operands) { 283 final int firstOperandIndex = getByteCodeForm().firstOperandIndex(); 284 final int byteCodeFormLength = getByteCodeForm().operandLength(); 285 if (firstOperandIndex < 1) { 286 // No operand rewriting permitted for this bytecode 287 throw new Error("Trying to rewrite " + this + " that has no rewrite"); 288 } 289 290 if (byteCodeFormLength != operands.length) { 291 throw new Error("Trying to rewrite " + this + " with " + operands.length + " but bytecode has length " + byteCodeForm.operandLength()); 292 } 293 294 for (int index = 0; index < byteCodeFormLength; index++) { 295 rewrite[index + firstOperandIndex] = operands[index] & 0xFF; 296 } 297 } 298 299 /** 300 * This is just like setOperandInt, but takes special care when the operand is less than 0 to make sure it's written correctly. 301 * 302 * @param operand int to set the rewrite bytes to 303 * @param position int position of the operands in the rewrite bytes 304 */ 305 public void setOperandSigned2Bytes(final int operand, final int position) { 306 if (operand >= 0) { 307 setOperand2Bytes(operand, position); 308 } else { 309 final int twosComplementOperand = 0x10000 + operand; 310 setOperand2Bytes(twosComplementOperand, position); 311 } 312 } 313 314 /** 315 * Some bytecodes (the ones with variable lengths) can't have a static rewrite array - they need the ability to update the array. This method permits that. 316 * 317 * Note that this should not be called from bytecodes which have a static rewrite; use the table in ByteCodeForm instead to specify those rewrites. 318 * 319 * @param rewrite Some bytecodes. 320 */ 321 public void setRewrite(final int[] rewrite) { 322 this.rewrite = rewrite; 323 } 324 325 @Override 326 public String toString() { 327 return getByteCodeForm().getName(); 328 } 329}