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