1/* 2 * Copyright (c) 2001, 2013, 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 26package com.sun.java.util.jar.pack; 27 28import com.sun.java.util.jar.pack.Package.Class; 29import java.lang.reflect.Modifier; 30import java.util.Arrays; 31import java.util.Collection; 32import static com.sun.java.util.jar.pack.Constants.*; 33 34/** 35 * Represents a chunk of bytecodes. 36 * @author John Rose 37 */ 38class 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 private static 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 refs.addAll(Arrays.asList(handler_class)); 150 if (fixups != null) { 151 fixups.visitRefs(refs); 152 } else { 153 // References (to a local cpMap) are embedded in the bytes. 154 ConstantPool.Entry[] cpMap = getCPMap(); 155 for (Instruction i = instructionAt(0); i != null; i = i.next()) { 156 if (verbose > 4) 157 System.out.println(i); 158 int cpref = i.getCPIndex(); 159 if (cpref >= 0) { 160 refs.add(cpMap[cpref]); 161 } 162 } 163 } 164 // Handle attribute list: 165 super.visitRefs(mode, refs); 166 } 167 168 // Since bytecodes are the single largest contributor to 169 // package size, it's worth a little bit of trouble 170 // to reduce the per-bytecode memory footprint. 171 // In the current scheme, half of the bulk of these arrays 172 // due to bytes, and half to shorts. (Ints are insignificant.) 173 // Given an average of 1.8 bytes per instruction, this means 174 // instruction boundary arrays are about a 75% overhead--tolerable. 175 // (By using bytes, we get 33% savings over just shorts and ints. 176 // Using both bytes and shorts gives 66% savings over just ints.) 177 static final boolean shrinkMaps = true; 178 179 private Object allocateInstructionMap(int[] insnMap, int mapLen) { 180 int PClimit = getLength(); 181 if (shrinkMaps && PClimit <= Byte.MAX_VALUE - Byte.MIN_VALUE) { 182 byte[] map = new byte[mapLen+1]; 183 for (int i = 0; i < mapLen; i++) { 184 map[i] = (byte)(insnMap[i] + Byte.MIN_VALUE); 185 } 186 map[mapLen] = (byte)(PClimit + Byte.MIN_VALUE); 187 return map; 188 } else if (shrinkMaps && PClimit < Short.MAX_VALUE - Short.MIN_VALUE) { 189 short[] map = new short[mapLen+1]; 190 for (int i = 0; i < mapLen; i++) { 191 map[i] = (short)(insnMap[i] + Short.MIN_VALUE); 192 } 193 map[mapLen] = (short)(PClimit + Short.MIN_VALUE); 194 return map; 195 } else { 196 int[] map = Arrays.copyOf(insnMap, mapLen + 1); 197 map[mapLen] = PClimit; 198 return map; 199 } 200 } 201 private int[] expandInstructionMap(Object map0) { 202 int[] imap; 203 if (map0 instanceof byte[]) { 204 byte[] map = (byte[]) map0; 205 imap = new int[map.length-1]; 206 for (int i = 0; i < imap.length; i++) { 207 imap[i] = map[i] - Byte.MIN_VALUE; 208 } 209 } else if (map0 instanceof short[]) { 210 short[] map = (short[]) 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 { 216 int[] map = (int[]) map0; 217 imap = Arrays.copyOfRange(map, 0, map.length - 1); 218 } 219 return imap; 220 } 221 222 Object getInsnMap() { 223 // Build a map of instruction boundaries. 224 if (insnMap != null) { 225 return insnMap; 226 } 227 int[] map = new int[getLength()]; 228 int fillp = 0; 229 for (Instruction i = instructionAt(0); i != null; i = i.next()) { 230 map[fillp++] = i.getPC(); 231 } 232 // Make it byte[], short[], or int[] according to the max BCI. 233 insnMap = allocateInstructionMap(map, fillp); 234 //assert(assertBCICodingsOK()); 235 return insnMap; 236 } 237 238 /** Encode the given BCI as an instruction boundary number. 239 * For completeness, irregular (non-boundary) BCIs are 240 * encoded compactly immediately after the boundary numbers. 241 * This encoding is the identity mapping outside 0..length, 242 * and it is 1-1 everywhere. All by itself this technique 243 * improved zipped rt.jar compression by 2.6%. 244 */ 245 public int encodeBCI(int bci) { 246 if (bci <= 0 || bci > getLength()) return bci; 247 Object map0 = getInsnMap(); 248 int i, len; 249 if (shrinkMaps && map0 instanceof byte[]) { 250 byte[] map = (byte[]) map0; 251 len = map.length; 252 i = Arrays.binarySearch(map, (byte)(bci + Byte.MIN_VALUE)); 253 } else if (shrinkMaps && map0 instanceof short[]) { 254 short[] map = (short[]) map0; 255 len = map.length; 256 i = Arrays.binarySearch(map, (short)(bci + Short.MIN_VALUE)); 257 } else { 258 int[] map = (int[]) map0; 259 len = map.length; 260 i = Arrays.binarySearch(map, bci); 261 } 262 assert(i != -1); 263 assert(i != 0); 264 assert(i != len); 265 assert(i != -len-1); 266 return (i >= 0) ? i : len + bci - (-i-1); 267 } 268 public int decodeBCI(int bciCode) { 269 if (bciCode <= 0 || bciCode > getLength()) return bciCode; 270 Object map0 = getInsnMap(); 271 int i, len; 272 // len == map.length 273 // If bciCode < len, result is map[bciCode], the common and fast case. 274 // Otherwise, let map[i] be the smallest map[*] larger than bci. 275 // Then, required by the return statement of encodeBCI: 276 // bciCode == len + bci - i 277 // Thus: 278 // bci-i == bciCode-len 279 // map[i]-adj-i == bciCode-len ; adj in (0..map[i]-map[i-1]) 280 // We can solve this by searching for adjacent entries 281 // map[i-1], map[i] such that: 282 // map[i-1]-(i-1) <= bciCode-len < map[i]-i 283 // This can be approximated by searching map[i] for bciCode and then 284 // linear searching backward. Given the right i, we then have: 285 // bci == bciCode-len + i 286 // This linear search is at its worst case for indexes in the beginning 287 // of a large method, but it's not clear that this is a problem in 288 // practice, since BCIs are usually on instruction boundaries. 289 if (shrinkMaps && map0 instanceof byte[]) { 290 byte[] map = (byte[]) map0; 291 len = map.length; 292 if (bciCode < len) 293 return map[bciCode] - Byte.MIN_VALUE; 294 i = Arrays.binarySearch(map, (byte)(bciCode + Byte.MIN_VALUE)); 295 if (i < 0) i = -i-1; 296 int key = bciCode-len + Byte.MIN_VALUE; 297 for (;; i--) { 298 if (map[i-1]-(i-1) <= key) break; 299 } 300 } else if (shrinkMaps && map0 instanceof short[]) { 301 short[] map = (short[]) map0; 302 len = map.length; 303 if (bciCode < len) 304 return map[bciCode] - Short.MIN_VALUE; 305 i = Arrays.binarySearch(map, (short)(bciCode + Short.MIN_VALUE)); 306 if (i < 0) i = -i-1; 307 int key = bciCode-len + Short.MIN_VALUE; 308 for (;; i--) { 309 if (map[i-1]-(i-1) <= key) break; 310 } 311 } else { 312 int[] map = (int[]) map0; 313 len = map.length; 314 if (bciCode < len) 315 return map[bciCode]; 316 i = Arrays.binarySearch(map, bciCode); 317 if (i < 0) i = -i-1; 318 int key = bciCode-len; 319 for (;; i--) { 320 if (map[i-1]-(i-1) <= key) break; 321 } 322 } 323 return bciCode-len + i; 324 } 325 326 public void finishRefs(ConstantPool.Index ix) { 327 if (fixups != null) { 328 fixups.finishRefs(ix); 329 fixups = null; 330 } 331 // Code attributes are finished in ClassWriter.writeAttributes. 332 } 333 334 Instruction instructionAt(int pc) { 335 return Instruction.at(bytes, pc); 336 } 337 338 static boolean flagsRequireCode(int flags) { 339 // A method's flags force it to have a Code attribute, 340 // if the flags are neither native nor abstract. 341 return (flags & (Modifier.NATIVE | Modifier.ABSTRACT)) == 0; 342 } 343 344 public String toString() { 345 return m+".Code"; 346 } 347 348 /// Fetching values from my own array. 349 public int getInt(int pc) { return Instruction.getInt(bytes, pc); } 350 public int getShort(int pc) { return Instruction.getShort(bytes, pc); } 351 public int getByte(int pc) { return Instruction.getByte(bytes, pc); } 352 void setInt(int pc, int x) { Instruction.setInt(bytes, pc, x); } 353 void setShort(int pc, int x) { Instruction.setShort(bytes, pc, x); } 354 void setByte(int pc, int x) { Instruction.setByte(bytes, pc, x); } 355 356/* TEST CODE ONLY 357 private boolean assertBCICodingsOK() { 358 boolean ok = true; 359 int len = java.lang.reflect.Array.getLength(insnMap); 360 int base = 0; 361 if (insnMap.getClass().getComponentType() == Byte.TYPE) 362 base = Byte.MIN_VALUE; 363 if (insnMap.getClass().getComponentType() == Short.TYPE) 364 base = Short.MIN_VALUE; 365 for (int i = -1, imax = getLength()+1; i <= imax; i++) { 366 int bci = i; 367 int enc = Math.min(-999, bci-1); 368 int dec = enc; 369 try { 370 enc = encodeBCI(bci); 371 dec = decodeBCI(enc); 372 } catch (RuntimeException ee) { 373 ee.printStackTrace(); 374 } 375 if (dec == bci) { 376 //System.out.println("BCI="+bci+(enc<len?"":" ")+" enc="+enc); 377 continue; 378 } 379 if (ok) { 380 for (int q = 0; q <= 1; q++) { 381 StringBuffer sb = new StringBuffer(); 382 sb.append("bci "+(q==0?"map":"del")+"["+len+"] = {"); 383 for (int j = 0; j < len; j++) { 384 int mapi = ((Number)java.lang.reflect.Array.get(insnMap, j)).intValue() - base; 385 mapi -= j*q; 386 sb.append(" "+mapi); 387 } 388 sb.append(" }"); 389 System.out.println("*** "+sb); 390 } 391 } 392 System.out.println("*** BCI="+bci+" enc="+enc+" dec="+dec); 393 ok = false; 394 } 395 return ok; 396 } 397//*/ 398} 399