--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/com/sun/java/util/jar/pack/Code.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2001, 2013, 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();
+ }
+
+ private static 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<Fixups.Fixup> 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);
+ 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;
+ }
+//*/
+}