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