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 }