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
26package com.sun.java.util.jar.pack;
27
28import com.sun.java.util.jar.pack.Package.Class;
29import java.lang.reflect.Modifier;
30import java.util.Arrays;
31import java.util.Collection;
32import static com.sun.java.util.jar.pack.Constants.*;
33
34/**
35 * Represents a chunk of bytecodes.
36 * @author John Rose
37 */
38class 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    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
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        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
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    }
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
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     */
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    }
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
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
334    Instruction instructionAt(int pc) {
335        return Instruction.at(bytes, pc);
336    }
337
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
344    public String toString() {
345        return m+".Code";
346    }
347
348    /// Fetching values from my own array.
349    public int getInt(int pc)    { return Instruction.getInt(bytes, pc); }
350    public int getShort(int pc)  { return Instruction.getShort(bytes, pc); }
351    public int getByte(int pc)   { return Instruction.getByte(bytes, pc); }
352    void setInt(int pc, int x)   { Instruction.setInt(bytes, pc, x); }
353    void setShort(int pc, int x) { Instruction.setShort(bytes, pc, 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