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 
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 
Code(Class.Method m)41     public Code(Class.Method m) {
42         this.m = m;
43     }
44 
getMethod()45     public Class.Method getMethod() {
46         return m;
47     }
thisClass()48     public Class thisClass() {
49         return m.thisClass();
50     }
getPackage()51     public Package getPackage() {
52         return m.thisClass().getPackage();
53     }
54 
getCPMap()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 
getLength()74     int getLength() { return bytes.length; }
75 
getMaxStack()76     int getMaxStack() {
77         return max_stack;
78     }
setMaxStack(int ms)79     void setMaxStack(int ms) {
80         max_stack = ms;
81     }
82 
getMaxNALocals()83     int getMaxNALocals() {
84         int argsize = m.getArgumentSize();
85         return max_locals - argsize;
86     }
setMaxNALocals(int ml)87     void setMaxNALocals(int ml) {
88         int argsize = m.getArgumentSize();
89         max_locals = argsize + ml;
90     }
91 
getHandlerCount()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     }
setHandlerCount(int h)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 
setBytes(byte[] bytes)108     void setBytes(byte[] bytes) {
109         this.bytes = bytes;
110         if (fixups != null)
111             fixups.setBytes(bytes);
112     }
113 
setInstructionMap(int[] insnMap, int mapLen)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     }
setInstructionMap(int[] insnMap)120     void setInstructionMap(int[] insnMap) {
121         setInstructionMap(insnMap, insnMap.length);
122     }
123 
getInstructionMap()124     int[] getInstructionMap() {
125         return expandInstructionMap(getInsnMap());
126     }
127 
addFixups(Collection<Fixups.Fixup> moreFixups)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 
trimToSize()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 
visitRefs(int mode, Collection<ConstantPool.Entry> refs)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 
allocateInstructionMap(int[] insnMap, int mapLen)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     }
expandInstructionMap(Object map0)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 
getInsnMap()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      */
encodeBCI(int bci)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     }
decodeBCI(int bciCode)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 
finishRefs(ConstantPool.Index ix)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 
instructionAt(int pc)334     Instruction instructionAt(int pc) {
335         return Instruction.at(bytes, pc);
336     }
337 
flagsRequireCode(int flags)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 
toString()344     public String toString() {
345         return m+".Code";
346     }
347 
348     /// Fetching values from my own array.
getInt(int pc)349     public int getInt(int pc)    { return Instruction.getInt(bytes, pc); }
getShort(int pc)350     public int getShort(int pc)  { return Instruction.getShort(bytes, pc); }
getByte(int pc)351     public int getByte(int pc)   { return Instruction.getByte(bytes, pc); }
setInt(int pc, int x)352     void setInt(int pc, int x)   { Instruction.setInt(bytes, pc, x); }
setShort(int pc, int x)353     void setShort(int pc, int x) { Instruction.setShort(bytes, pc, x); }
setByte(int pc, int 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