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