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 }