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