1 /* 2 * Copyright (c) 2001, 2003, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.java.util.jar.pack; 27 28 import com.sun.java.util.jar.pack.Package.Class; 29 import java.lang.reflect.Modifier; 30 import java.util.Arrays; 31 import java.util.Collection; 32 33 /** 34 * Represents a chunk of bytecodes. 35 * @author John Rose 36 */ 37 class Code extends Attribute.Holder implements Constants { 38 Class.Method m; 39 40 public Code(Class.Method m) { 41 this.m = m; 42 } 43 44 public Class.Method getMethod() { 45 return m; 46 } 47 public Class thisClass() { 48 return m.thisClass(); 49 } 50 public Package getPackage() { 51 return m.thisClass().getPackage(); 52 } 53 54 public ConstantPool.Entry[] getCPMap() { 55 return m.getCPMap(); 56 } 57 58 static private final ConstantPool.Entry[] noRefs = ConstantPool.noRefs; 59 60 // The following fields are used directly by the ClassReader, etc. 61 int max_stack; 62 int max_locals; 63 64 ConstantPool.Entry handler_class[] = noRefs; 65 int handler_start[] = noInts; 66 int handler_end[] = noInts; 67 int handler_catch[] = noInts; 68 69 byte[] bytes; 70 Fixups fixups; // reference relocations, if any are required 71 Object insnMap; // array of instruction boundaries 72 73 int getLength() { return bytes.length; } 74 75 int getMaxStack() { 76 return max_stack; 77 } 78 void setMaxStack(int ms) { 79 max_stack = ms; 80 } 81 82 int getMaxNALocals() { 83 int argsize = m.getArgumentSize(); 84 return max_locals - argsize; 85 } 86 void setMaxNALocals(int ml) { 87 int argsize = m.getArgumentSize(); 88 max_locals = argsize + ml; 89 } 90 91 int getHandlerCount() { 92 assert(handler_class.length == handler_start.length); 93 assert(handler_class.length == handler_end.length); 94 assert(handler_class.length == handler_catch.length); 95 return handler_class.length; 96 } 97 void setHandlerCount(int h) { 98 if (h > 0) { 99 handler_class = new ConstantPool.Entry[h]; 100 handler_start = new int[h]; 101 handler_end = new int[h]; 102 handler_catch = new int[h]; 103 // caller must fill these in ASAP 104 } 105 } 106 107 void setBytes(byte[] bytes) { 108 this.bytes = bytes; 109 if (fixups != null) 110 fixups.setBytes(bytes); 111 } 112 113 void setInstructionMap(int[] insnMap, int mapLen) { 114 //int[] oldMap = null; 115 //assert((oldMap = getInstructionMap()) != null); 116 this.insnMap = allocateInstructionMap(insnMap, mapLen); 117 //assert(Arrays.equals(oldMap, getInstructionMap())); 118 } 119 void setInstructionMap(int[] insnMap) { 120 setInstructionMap(insnMap, insnMap.length); 121 } 122 123 int[] getInstructionMap() { 124 return expandInstructionMap(getInsnMap()); 125 } 126 127 void addFixups(Collection moreFixups) { 128 if (fixups == null) { 129 fixups = new Fixups(bytes); 130 } 131 assert(fixups.getBytes() == bytes); 132 fixups.addAll(moreFixups); 133 } 134 135 public void trimToSize() { 136 if (fixups != null) { 137 fixups.trimToSize(); 138 if (fixups.size() == 0) 139 fixups = null; 140 } 141 super.trimToSize(); 142 } 143 144 protected void visitRefs(int mode, Collection refs) { 145 int verbose = getPackage().verbose; 146 if (verbose > 2) 147 System.out.println("Reference scan "+this); 148 Class cls = thisClass(); 149 Package pkg = cls.getPackage(); 150 for (int i = 0; i < handler_class.length; i++) { 151 refs.add(handler_class[i]); 152 } 153 if (fixups != null) { 154 fixups.visitRefs(refs); 155 } else { 156 // References (to a local cpMap) are embedded in the bytes. 157 ConstantPool.Entry[] cpMap = getCPMap(); 158 for (Instruction i = instructionAt(0); i != null; i = i.next()) { 159 if (verbose > 4) 160 System.out.println(i); 161 int cpref = i.getCPIndex(); 162 if (cpref >= 0) { 163 refs.add(cpMap[cpref]); 164 } 165 } 166 } 167 // Handle attribute list: 168 super.visitRefs(mode, refs); 169 } 170 171 // Since bytecodes are the single largest contributor to 172 // package size, it's worth a little bit of trouble 173 // to reduce the per-bytecode memory footprint. 174 // In the current scheme, half of the bulk of these arrays 175 // due to bytes, and half to shorts. (Ints are insignificant.) 176 // Given an average of 1.8 bytes per instruction, this means 177 // instruction boundary arrays are about a 75% overhead--tolerable. 178 // (By using bytes, we get 33% savings over just shorts and ints. 179 // Using both bytes and shorts gives 66% savings over just ints.) 180 static final boolean shrinkMaps = true; 181 182 private Object allocateInstructionMap(int[] insnMap, int mapLen) { 183 int PClimit = getLength(); 184 if (shrinkMaps && PClimit <= Byte.MAX_VALUE - Byte.MIN_VALUE) { 185 byte[] map = new byte[mapLen+1]; 186 for (int i = 0; i < mapLen; i++) { 187 map[i] = (byte)(insnMap[i] + Byte.MIN_VALUE); 188 } 189 map[mapLen] = (byte)(PClimit + Byte.MIN_VALUE); 190 return map; 191 } else if (shrinkMaps && PClimit < Short.MAX_VALUE - Short.MIN_VALUE) { 192 short[] map = new short[mapLen+1]; 193 for (int i = 0; i < mapLen; i++) { 194 map[i] = (short)(insnMap[i] + Short.MIN_VALUE); 195 } 196 map[mapLen] = (short)(PClimit + Short.MIN_VALUE); 197 return map; 198 } else { 199 int[] map = new int[mapLen+1]; 200 for (int i = 0; i < mapLen; i++) { 201 map[i] = (int) insnMap[i]; 202 } 203 map[mapLen] = (int) PClimit; 204 return map; 205 } 206 } 207 private int[] expandInstructionMap(Object map0) { 208 int[] imap; 209 if (map0 instanceof byte[]) { 210 byte[] map = (byte[]) map0; 211 imap = new int[map.length-1]; 212 for (int i = 0; i < imap.length; i++) { 213 imap[i] = map[i] - Byte.MIN_VALUE; 214 } 215 } else if (map0 instanceof short[]) { 216 short[] map = (short[]) map0; 217 imap = new int[map.length-1]; 218 for (int i = 0; i < imap.length; i++) { 219 imap[i] = map[i] - Byte.MIN_VALUE; 220 } 221 } else { 222 int[] map = (int[]) map0; 223 imap = new int[map.length-1]; 224 for (int i = 0; i < imap.length; i++) { 225 imap[i] = map[i]; 226 } 227 } 228 return imap; 229 } 230 231 Object getInsnMap() { 232 // Build a map of instruction boundaries. 233 if (insnMap != null) { 234 return insnMap; 235 } 236 int[] map = new int[getLength()]; 237 int fillp = 0; 238 for (Instruction i = instructionAt(0); i != null; i = i.next()) { 239 map[fillp++] = i.getPC(); 240 } 241 // Make it byte[], short[], or int[] according to the max BCI. 242 insnMap = allocateInstructionMap(map, fillp); 243 //assert(assertBCICodingsOK()); 244 return insnMap; 245 } 246 247 /** Encode the given BCI as an instruction boundary number. 248 * For completeness, irregular (non-boundary) BCIs are 249 * encoded compactly immediately after the boundary numbers. 250 * This encoding is the identity mapping outside 0..length, 251 * and it is 1-1 everywhere. All by itself this technique 252 * improved zipped rt.jar compression by 2.6%. 253 */ 254 public int encodeBCI(int bci) { 255 if (bci <= 0 || bci > getLength()) return bci; 256 Object map0 = getInsnMap(); 257 int i, len; 258 if (shrinkMaps && map0 instanceof byte[]) { 259 byte[] map = (byte[]) map0; 260 len = map.length; 261 i = Arrays.binarySearch(map, (byte)(bci + Byte.MIN_VALUE)); 262 } else if (shrinkMaps && map0 instanceof short[]) { 263 short[] map = (short[]) map0; 264 len = map.length; 265 i = Arrays.binarySearch(map, (short)(bci + Short.MIN_VALUE)); 266 } else { 267 int[] map = (int[]) map0; 268 len = map.length; 269 i = Arrays.binarySearch(map, (int)bci); 270 } 271 assert(i != -1); 272 assert(i != 0); 273 assert(i != len); 274 assert(i != -len-1); 275 return (i >= 0) ? i : len + bci - (-i-1); 276 } 277 public int decodeBCI(int bciCode) { 278 if (bciCode <= 0 || bciCode > getLength()) return bciCode; 279 Object map0 = getInsnMap(); 280 int i, len; 281 // len == map.length 282 // If bciCode < len, result is map[bciCode], the common and fast case. 283 // Otherwise, let map[i] be the smallest map[*] larger than bci. 284 // Then, required by the return statement of encodeBCI: 285 // bciCode == len + bci - i 286 // Thus: 287 // bci-i == bciCode-len 288 // map[i]-adj-i == bciCode-len ; adj in (0..map[i]-map[i-1]) 289 // We can solve this by searching for adjacent entries 290 // map[i-1], map[i] such that: 291 // map[i-1]-(i-1) <= bciCode-len < map[i]-i 292 // This can be approximated by searching map[i] for bciCode and then 293 // linear searching backward. Given the right i, we then have: 294 // bci == bciCode-len + i 295 // This linear search is at its worst case for indexes in the beginning 296 // of a large method, but it's not clear that this is a problem in 297 // practice, since BCIs are usually on instruction boundaries. 298 if (shrinkMaps && map0 instanceof byte[]) { 299 byte[] map = (byte[]) map0; 300 len = map.length; 301 if (bciCode < len) 302 return map[bciCode] - Byte.MIN_VALUE; 303 i = Arrays.binarySearch(map, (byte)(bciCode + Byte.MIN_VALUE)); 304 if (i < 0) i = -i-1; 305 int key = bciCode-len + Byte.MIN_VALUE; 306 for (;; i--) { 307 if (map[i-1]-(i-1) <= key) break; 308 } 309 } else if (shrinkMaps && map0 instanceof short[]) { 310 short[] map = (short[]) map0; 311 len = map.length; 312 if (bciCode < len) 313 return map[bciCode] - Short.MIN_VALUE; 314 i = Arrays.binarySearch(map, (short)(bciCode + Short.MIN_VALUE)); 315 if (i < 0) i = -i-1; 316 int key = bciCode-len + Short.MIN_VALUE; 317 for (;; i--) { 318 if (map[i-1]-(i-1) <= key) break; 319 } 320 } else { 321 int[] map = (int[]) map0; 322 len = map.length; 323 if (bciCode < len) 324 return map[bciCode]; 325 i = Arrays.binarySearch(map, (int)bciCode); 326 if (i < 0) i = -i-1; 327 int key = bciCode-len; 328 for (;; i--) { 329 if (map[i-1]-(i-1) <= key) break; 330 } 331 } 332 return bciCode-len + i; 333 } 334 335 public void finishRefs(ConstantPool.Index ix) { 336 if (fixups != null) { 337 fixups.finishRefs(ix); 338 fixups = null; 339 } 340 // Code attributes are finished in ClassWriter.writeAttributes. 341 } 342 343 Instruction instructionAt(int pc) { 344 return Instruction.at(bytes, pc); 345 } 346 347 static boolean flagsRequireCode(int flags) { 348 // A method's flags force it to have a Code attribute, 349 // if the flags are neither native nor abstract. 350 return (flags & (Modifier.NATIVE | Modifier.ABSTRACT)) == 0; 351 } 352 353 public String toString() { 354 return m+".Code"; 355 } 356 357 /// Fetching values from my own array. 358 public int getInt(int pc) { return Instruction.getInt(bytes, pc); } 359 public int getShort(int pc) { return Instruction.getShort(bytes, pc); } 360 public int getByte(int pc) { return Instruction.getByte(bytes, pc); } 361 void setInt(int pc, int x) { Instruction.setInt(bytes, pc, x); } 362 void setShort(int pc, int x) { Instruction.setShort(bytes, pc, x); } 363 void setByte(int pc, int x) { Instruction.setByte(bytes, pc, x); } 364 365 /* TEST CODE ONLY 366 private boolean assertBCICodingsOK() { 367 boolean ok = true; 368 int len = java.lang.reflect.Array.getLength(insnMap); 369 int base = 0; 370 if (insnMap.getClass().getComponentType() == Byte.TYPE) 371 base = Byte.MIN_VALUE; 372 if (insnMap.getClass().getComponentType() == Short.TYPE) 373 base = Short.MIN_VALUE; 374 for (int i = -1, imax = getLength()+1; i <= imax; i++) { 375 int bci = i; 376 int enc = Math.min(-999, bci-1); 377 int dec = enc; 378 try { 379 enc = encodeBCI(bci); 380 dec = decodeBCI(enc); 381 } catch (RuntimeException ee) { 382 ee.printStackTrace(); 383 } 384 if (dec == bci) { 385 //System.out.println("BCI="+bci+(enc<len?"":" ")+" enc="+enc); 386 continue; 387 } 388 if (ok) { 389 for (int q = 0; q <= 1; q++) { 390 StringBuffer sb = new StringBuffer(); 391 sb.append("bci "+(q==0?"map":"del")+"["+len+"] = {"); 392 for (int j = 0; j < len; j++) { 393 int mapi = ((Number)java.lang.reflect.Array.get(insnMap, j)).intValue() - base; 394 mapi -= j*q; 395 sb.append(" "+mapi); 396 } 397 sb.append(" }"); 398 System.out.println("*** "+sb); 399 } 400 } 401 System.out.println("*** BCI="+bci+" enc="+enc+" dec="+dec); 402 ok = false; 403 } 404 return ok; 405 } 406 //*/ 407 }