| /* |
| * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package com.sun.java.util.jar.pack; |
| |
| import com.sun.java.util.jar.pack.Package.Class; |
| import java.lang.reflect.Modifier; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import static com.sun.java.util.jar.pack.Constants.*; |
| |
| /** |
| * Represents a chunk of bytecodes. |
| * @author John Rose |
| */ |
| class Code extends Attribute.Holder { |
| Class.Method m; |
| |
| public Code(Class.Method m) { |
| this.m = m; |
| } |
| |
| public Class.Method getMethod() { |
| return m; |
| } |
| public Class thisClass() { |
| return m.thisClass(); |
| } |
| public Package getPackage() { |
| return m.thisClass().getPackage(); |
| } |
| |
| public ConstantPool.Entry[] getCPMap() { |
| return m.getCPMap(); |
| } |
| |
| static private final ConstantPool.Entry[] noRefs = ConstantPool.noRefs; |
| |
| // The following fields are used directly by the ClassReader, etc. |
| int max_stack; |
| int max_locals; |
| |
| ConstantPool.Entry handler_class[] = noRefs; |
| int handler_start[] = noInts; |
| int handler_end[] = noInts; |
| int handler_catch[] = noInts; |
| |
| byte[] bytes; |
| Fixups fixups; // reference relocations, if any are required |
| Object insnMap; // array of instruction boundaries |
| |
| int getLength() { return bytes.length; } |
| |
| int getMaxStack() { |
| return max_stack; |
| } |
| void setMaxStack(int ms) { |
| max_stack = ms; |
| } |
| |
| int getMaxNALocals() { |
| int argsize = m.getArgumentSize(); |
| return max_locals - argsize; |
| } |
| void setMaxNALocals(int ml) { |
| int argsize = m.getArgumentSize(); |
| max_locals = argsize + ml; |
| } |
| |
| int getHandlerCount() { |
| assert(handler_class.length == handler_start.length); |
| assert(handler_class.length == handler_end.length); |
| assert(handler_class.length == handler_catch.length); |
| return handler_class.length; |
| } |
| void setHandlerCount(int h) { |
| if (h > 0) { |
| handler_class = new ConstantPool.Entry[h]; |
| handler_start = new int[h]; |
| handler_end = new int[h]; |
| handler_catch = new int[h]; |
| // caller must fill these in ASAP |
| } |
| } |
| |
| void setBytes(byte[] bytes) { |
| this.bytes = bytes; |
| if (fixups != null) |
| fixups.setBytes(bytes); |
| } |
| |
| void setInstructionMap(int[] insnMap, int mapLen) { |
| //int[] oldMap = null; |
| //assert((oldMap = getInstructionMap()) != null); |
| this.insnMap = allocateInstructionMap(insnMap, mapLen); |
| //assert(Arrays.equals(oldMap, getInstructionMap())); |
| } |
| void setInstructionMap(int[] insnMap) { |
| setInstructionMap(insnMap, insnMap.length); |
| } |
| |
| int[] getInstructionMap() { |
| return expandInstructionMap(getInsnMap()); |
| } |
| |
| void addFixups(Collection moreFixups) { |
| if (fixups == null) { |
| fixups = new Fixups(bytes); |
| } |
| assert(fixups.getBytes() == bytes); |
| fixups.addAll(moreFixups); |
| } |
| |
| public void trimToSize() { |
| if (fixups != null) { |
| fixups.trimToSize(); |
| if (fixups.size() == 0) |
| fixups = null; |
| } |
| super.trimToSize(); |
| } |
| |
| protected void visitRefs(int mode, Collection<ConstantPool.Entry> refs) { |
| int verbose = getPackage().verbose; |
| if (verbose > 2) |
| System.out.println("Reference scan "+this); |
| Class cls = thisClass(); |
| refs.addAll(Arrays.asList(handler_class)); |
| if (fixups != null) { |
| fixups.visitRefs(refs); |
| } else { |
| // References (to a local cpMap) are embedded in the bytes. |
| ConstantPool.Entry[] cpMap = getCPMap(); |
| for (Instruction i = instructionAt(0); i != null; i = i.next()) { |
| if (verbose > 4) |
| System.out.println(i); |
| int cpref = i.getCPIndex(); |
| if (cpref >= 0) { |
| refs.add(cpMap[cpref]); |
| } |
| } |
| } |
| // Handle attribute list: |
| super.visitRefs(mode, refs); |
| } |
| |
| // Since bytecodes are the single largest contributor to |
| // package size, it's worth a little bit of trouble |
| // to reduce the per-bytecode memory footprint. |
| // In the current scheme, half of the bulk of these arrays |
| // due to bytes, and half to shorts. (Ints are insignificant.) |
| // Given an average of 1.8 bytes per instruction, this means |
| // instruction boundary arrays are about a 75% overhead--tolerable. |
| // (By using bytes, we get 33% savings over just shorts and ints. |
| // Using both bytes and shorts gives 66% savings over just ints.) |
| static final boolean shrinkMaps = true; |
| |
| private Object allocateInstructionMap(int[] insnMap, int mapLen) { |
| int PClimit = getLength(); |
| if (shrinkMaps && PClimit <= Byte.MAX_VALUE - Byte.MIN_VALUE) { |
| byte[] map = new byte[mapLen+1]; |
| for (int i = 0; i < mapLen; i++) { |
| map[i] = (byte)(insnMap[i] + Byte.MIN_VALUE); |
| } |
| map[mapLen] = (byte)(PClimit + Byte.MIN_VALUE); |
| return map; |
| } else if (shrinkMaps && PClimit < Short.MAX_VALUE - Short.MIN_VALUE) { |
| short[] map = new short[mapLen+1]; |
| for (int i = 0; i < mapLen; i++) { |
| map[i] = (short)(insnMap[i] + Short.MIN_VALUE); |
| } |
| map[mapLen] = (short)(PClimit + Short.MIN_VALUE); |
| return map; |
| } else { |
| int[] map = Arrays.copyOf(insnMap, mapLen + 1); |
| map[mapLen] = PClimit; |
| return map; |
| } |
| } |
| private int[] expandInstructionMap(Object map0) { |
| int[] imap; |
| if (map0 instanceof byte[]) { |
| byte[] map = (byte[]) map0; |
| imap = new int[map.length-1]; |
| for (int i = 0; i < imap.length; i++) { |
| imap[i] = map[i] - Byte.MIN_VALUE; |
| } |
| } else if (map0 instanceof short[]) { |
| short[] map = (short[]) map0; |
| imap = new int[map.length-1]; |
| for (int i = 0; i < imap.length; i++) { |
| imap[i] = map[i] - Byte.MIN_VALUE; |
| } |
| } else { |
| int[] map = (int[]) map0; |
| imap = Arrays.copyOfRange(map, 0, map.length - 1); |
| } |
| return imap; |
| } |
| |
| Object getInsnMap() { |
| // Build a map of instruction boundaries. |
| if (insnMap != null) { |
| return insnMap; |
| } |
| int[] map = new int[getLength()]; |
| int fillp = 0; |
| for (Instruction i = instructionAt(0); i != null; i = i.next()) { |
| map[fillp++] = i.getPC(); |
| } |
| // Make it byte[], short[], or int[] according to the max BCI. |
| insnMap = allocateInstructionMap(map, fillp); |
| //assert(assertBCICodingsOK()); |
| return insnMap; |
| } |
| |
| /** Encode the given BCI as an instruction boundary number. |
| * For completeness, irregular (non-boundary) BCIs are |
| * encoded compactly immediately after the boundary numbers. |
| * This encoding is the identity mapping outside 0..length, |
| * and it is 1-1 everywhere. All by itself this technique |
| * improved zipped rt.jar compression by 2.6%. |
| */ |
| public int encodeBCI(int bci) { |
| if (bci <= 0 || bci > getLength()) return bci; |
| Object map0 = getInsnMap(); |
| int i, len; |
| if (shrinkMaps && map0 instanceof byte[]) { |
| byte[] map = (byte[]) map0; |
| len = map.length; |
| i = Arrays.binarySearch(map, (byte)(bci + Byte.MIN_VALUE)); |
| } else if (shrinkMaps && map0 instanceof short[]) { |
| short[] map = (short[]) map0; |
| len = map.length; |
| i = Arrays.binarySearch(map, (short)(bci + Short.MIN_VALUE)); |
| } else { |
| int[] map = (int[]) map0; |
| len = map.length; |
| i = Arrays.binarySearch(map, bci); |
| } |
| assert(i != -1); |
| assert(i != 0); |
| assert(i != len); |
| assert(i != -len-1); |
| return (i >= 0) ? i : len + bci - (-i-1); |
| } |
| public int decodeBCI(int bciCode) { |
| if (bciCode <= 0 || bciCode > getLength()) return bciCode; |
| Object map0 = getInsnMap(); |
| int i, len; |
| // len == map.length |
| // If bciCode < len, result is map[bciCode], the common and fast case. |
| // Otherwise, let map[i] be the smallest map[*] larger than bci. |
| // Then, required by the return statement of encodeBCI: |
| // bciCode == len + bci - i |
| // Thus: |
| // bci-i == bciCode-len |
| // map[i]-adj-i == bciCode-len ; adj in (0..map[i]-map[i-1]) |
| // We can solve this by searching for adjacent entries |
| // map[i-1], map[i] such that: |
| // map[i-1]-(i-1) <= bciCode-len < map[i]-i |
| // This can be approximated by searching map[i] for bciCode and then |
| // linear searching backward. Given the right i, we then have: |
| // bci == bciCode-len + i |
| // This linear search is at its worst case for indexes in the beginning |
| // of a large method, but it's not clear that this is a problem in |
| // practice, since BCIs are usually on instruction boundaries. |
| if (shrinkMaps && map0 instanceof byte[]) { |
| byte[] map = (byte[]) map0; |
| len = map.length; |
| if (bciCode < len) |
| return map[bciCode] - Byte.MIN_VALUE; |
| i = Arrays.binarySearch(map, (byte)(bciCode + Byte.MIN_VALUE)); |
| if (i < 0) i = -i-1; |
| int key = bciCode-len + Byte.MIN_VALUE; |
| for (;; i--) { |
| if (map[i-1]-(i-1) <= key) break; |
| } |
| } else if (shrinkMaps && map0 instanceof short[]) { |
| short[] map = (short[]) map0; |
| len = map.length; |
| if (bciCode < len) |
| return map[bciCode] - Short.MIN_VALUE; |
| i = Arrays.binarySearch(map, (short)(bciCode + Short.MIN_VALUE)); |
| if (i < 0) i = -i-1; |
| int key = bciCode-len + Short.MIN_VALUE; |
| for (;; i--) { |
| if (map[i-1]-(i-1) <= key) break; |
| } |
| } else { |
| int[] map = (int[]) map0; |
| len = map.length; |
| if (bciCode < len) |
| return map[bciCode]; |
| i = Arrays.binarySearch(map, bciCode); |
| if (i < 0) i = -i-1; |
| int key = bciCode-len; |
| for (;; i--) { |
| if (map[i-1]-(i-1) <= key) break; |
| } |
| } |
| return bciCode-len + i; |
| } |
| |
| public void finishRefs(ConstantPool.Index ix) { |
| if (fixups != null) { |
| fixups.finishRefs(ix); |
| fixups = null; |
| } |
| // Code attributes are finished in ClassWriter.writeAttributes. |
| } |
| |
| Instruction instructionAt(int pc) { |
| return Instruction.at(bytes, pc); |
| } |
| |
| static boolean flagsRequireCode(int flags) { |
| // A method's flags force it to have a Code attribute, |
| // if the flags are neither native nor abstract. |
| return (flags & (Modifier.NATIVE | Modifier.ABSTRACT)) == 0; |
| } |
| |
| public String toString() { |
| return m+".Code"; |
| } |
| |
| /// Fetching values from my own array. |
| public int getInt(int pc) { return Instruction.getInt(bytes, pc); } |
| public int getShort(int pc) { return Instruction.getShort(bytes, pc); } |
| public int getByte(int pc) { return Instruction.getByte(bytes, pc); } |
| void setInt(int pc, int x) { Instruction.setInt(bytes, pc, x); } |
| void setShort(int pc, int x) { Instruction.setShort(bytes, pc, x); } |
| void setByte(int pc, int x) { Instruction.setByte(bytes, pc, x); } |
| |
| /* TEST CODE ONLY |
| private boolean assertBCICodingsOK() { |
| boolean ok = true; |
| int len = java.lang.reflect.Array.getLength(insnMap); |
| int base = 0; |
| if (insnMap.getClass().getComponentType() == Byte.TYPE) |
| base = Byte.MIN_VALUE; |
| if (insnMap.getClass().getComponentType() == Short.TYPE) |
| base = Short.MIN_VALUE; |
| for (int i = -1, imax = getLength()+1; i <= imax; i++) { |
| int bci = i; |
| int enc = Math.min(-999, bci-1); |
| int dec = enc; |
| try { |
| enc = encodeBCI(bci); |
| dec = decodeBCI(enc); |
| } catch (RuntimeException ee) { |
| ee.printStackTrace(); |
| } |
| if (dec == bci) { |
| //System.out.println("BCI="+bci+(enc<len?"":" ")+" enc="+enc); |
| continue; |
| } |
| if (ok) { |
| for (int q = 0; q <= 1; q++) { |
| StringBuffer sb = new StringBuffer(); |
| sb.append("bci "+(q==0?"map":"del")+"["+len+"] = {"); |
| for (int j = 0; j < len; j++) { |
| int mapi = ((Number)java.lang.reflect.Array.get(insnMap, j)).intValue() - base; |
| mapi -= j*q; |
| sb.append(" "+mapi); |
| } |
| sb.append(" }"); |
| System.out.println("*** "+sb); |
| } |
| } |
| System.out.println("*** BCI="+bci+" enc="+enc+" dec="+dec); |
| ok = false; |
| } |
| return ok; |
| } |
| //*/ |
| } |