001/***
002 * ASM: a very small and fast Java bytecode manipulation framework
003 * Copyright (c) 2000-2011 INRIA, France Telecom
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions
008 * are met:
009 * 1. Redistributions of source code must retain the above copyright
010 *    notice, this list of conditions and the following disclaimer.
011 * 2. Redistributions in binary form must reproduce the above copyright
012 *    notice, this list of conditions and the following disclaimer in the
013 *    documentation and/or other materials provided with the distribution.
014 * 3. Neither the name of the copyright holders nor the names of its
015 *    contributors may be used to endorse or promote products derived from
016 *    this software without specific prior written permission.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
028 * THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package io.ebean.enhance.asm.tree.analysis;
031
032import io.ebean.enhance.asm.Opcodes;
033import io.ebean.enhance.asm.Type;
034import io.ebean.enhance.asm.tree.AbstractInsnNode;
035import io.ebean.enhance.asm.tree.IincInsnNode;
036import io.ebean.enhance.asm.tree.InsnList;
037import io.ebean.enhance.asm.tree.JumpInsnNode;
038import io.ebean.enhance.asm.tree.LabelNode;
039import io.ebean.enhance.asm.tree.LookupSwitchInsnNode;
040import io.ebean.enhance.asm.tree.MethodNode;
041import io.ebean.enhance.asm.tree.TableSwitchInsnNode;
042import io.ebean.enhance.asm.tree.TryCatchBlockNode;
043import io.ebean.enhance.asm.tree.VarInsnNode;
044
045import java.util.ArrayList;
046import java.util.HashMap;
047import java.util.List;
048import java.util.Map;
049
050/**
051 * A semantic bytecode analyzer. <i>This class does not fully check that JSR and
052 * RET instructions are valid.</i>
053 * 
054 * @param <V>
055 *            type of the Value used for the analysis.
056 * 
057 * @author Eric Bruneton
058 */
059public class Analyzer<V extends Value> implements Opcodes {
060
061    private final Interpreter<V> interpreter;
062
063    private int n;
064
065    private InsnList insns;
066
067    private List<TryCatchBlockNode>[] handlers;
068
069    private Frame<V>[] frames;
070
071    private Subroutine[] subroutines;
072
073    private boolean[] queued;
074
075    private int[] queue;
076
077    private int top;
078
079    /**
080     * Constructs a new {@link Analyzer}.
081     * 
082     * @param interpreter
083     *            the interpreter to be used to symbolically interpret the
084     *            bytecode instructions.
085     */
086    public Analyzer(final Interpreter<V> interpreter) {
087        this.interpreter = interpreter;
088    }
089
090    /**
091     * Analyzes the given method.
092     * 
093     * @param owner
094     *            the internal name of the class to which the method belongs.
095     * @param m
096     *            the method to be analyzed.
097     * @return the symbolic state of the execution stack frame at each bytecode
098     *         instruction of the method. The size of the returned array is
099     *         equal to the number of instructions (and labels) of the method. A
100     *         given frame is <tt>null</tt> if and only if the corresponding
101     *         instruction cannot be reached (dead code).
102     * @throws AnalyzerException
103     *             if a problem occurs during the analysis.
104     */
105    @SuppressWarnings("unchecked")
106    public Frame<V>[] analyze(final String owner, final MethodNode m)
107            throws AnalyzerException {
108        if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {
109            frames = (Frame<V>[]) new Frame<?>[0];
110            return frames;
111        }
112        n = m.instructions.size();
113        insns = m.instructions;
114        handlers = (List<TryCatchBlockNode>[]) new List<?>[n];
115        frames = (Frame<V>[]) new Frame<?>[n];
116        subroutines = new Subroutine[n];
117        queued = new boolean[n];
118        queue = new int[n];
119        top = 0;
120
121        // computes exception handlers for each instruction
122        for (int i = 0; i < m.tryCatchBlocks.size(); ++i) {
123            TryCatchBlockNode tcb = m.tryCatchBlocks.get(i);
124            int begin = insns.indexOf(tcb.start);
125            int end = insns.indexOf(tcb.end);
126            for (int j = begin; j < end; ++j) {
127                List<TryCatchBlockNode> insnHandlers = handlers[j];
128                if (insnHandlers == null) {
129                    insnHandlers = new ArrayList<TryCatchBlockNode>();
130                    handlers[j] = insnHandlers;
131                }
132                insnHandlers.add(tcb);
133            }
134        }
135
136        // computes the subroutine for each instruction:
137        Subroutine main = new Subroutine(null, m.maxLocals, null);
138        List<AbstractInsnNode> subroutineCalls = new ArrayList<AbstractInsnNode>();
139        Map<LabelNode, Subroutine> subroutineHeads = new HashMap<LabelNode, Subroutine>();
140        findSubroutine(0, main, subroutineCalls);
141        while (!subroutineCalls.isEmpty()) {
142            JumpInsnNode jsr = (JumpInsnNode) subroutineCalls.remove(0);
143            Subroutine sub = subroutineHeads.get(jsr.label);
144            if (sub == null) {
145                sub = new Subroutine(jsr.label, m.maxLocals, jsr);
146                subroutineHeads.put(jsr.label, sub);
147                findSubroutine(insns.indexOf(jsr.label), sub, subroutineCalls);
148            } else {
149                sub.callers.add(jsr);
150            }
151        }
152        for (int i = 0; i < n; ++i) {
153            if (subroutines[i] != null && subroutines[i].start == null) {
154                subroutines[i] = null;
155            }
156        }
157
158        // initializes the data structures for the control flow analysis
159        Frame<V> current = newFrame(m.maxLocals, m.maxStack);
160        Frame<V> handler = newFrame(m.maxLocals, m.maxStack);
161        current.setReturn(interpreter.newValue(Type.getReturnType(m.desc)));
162        Type[] args = Type.getArgumentTypes(m.desc);
163        int local = 0;
164        if ((m.access & ACC_STATIC) == 0) {
165            Type ctype = Type.getObjectType(owner);
166            current.setLocal(local++, interpreter.newValue(ctype));
167        }
168        for (int i = 0; i < args.length; ++i) {
169            current.setLocal(local++, interpreter.newValue(args[i]));
170            if (args[i].getSize() == 2) {
171                current.setLocal(local++, interpreter.newValue(null));
172            }
173        }
174        while (local < m.maxLocals) {
175            current.setLocal(local++, interpreter.newValue(null));
176        }
177        merge(0, current, null);
178
179        init(owner, m);
180
181        // control flow analysis
182        while (top > 0) {
183            int insn = queue[--top];
184            Frame<V> f = frames[insn];
185            Subroutine subroutine = subroutines[insn];
186            queued[insn] = false;
187
188            AbstractInsnNode insnNode = null;
189            try {
190                insnNode = m.instructions.get(insn);
191                int insnOpcode = insnNode.getOpcode();
192                int insnType = insnNode.getType();
193
194                if (insnType == AbstractInsnNode.LABEL
195                        || insnType == AbstractInsnNode.LINE
196                        || insnType == AbstractInsnNode.FRAME) {
197                    merge(insn + 1, f, subroutine);
198                    newControlFlowEdge(insn, insn + 1);
199                } else {
200                    current.init(f).execute(insnNode, interpreter);
201                    subroutine = subroutine == null ? null : subroutine.copy();
202
203                    if (insnNode instanceof JumpInsnNode) {
204                        JumpInsnNode j = (JumpInsnNode) insnNode;
205                        if (insnOpcode != GOTO && insnOpcode != JSR) {
206                            merge(insn + 1, current, subroutine);
207                            newControlFlowEdge(insn, insn + 1);
208                        }
209                        int jump = insns.indexOf(j.label);
210                        if (insnOpcode == JSR) {
211                            merge(jump, current, new Subroutine(j.label,
212                                    m.maxLocals, j));
213                        } else {
214                            merge(jump, current, subroutine);
215                        }
216                        newControlFlowEdge(insn, jump);
217                    } else if (insnNode instanceof LookupSwitchInsnNode) {
218                        LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode;
219                        int jump = insns.indexOf(lsi.dflt);
220                        merge(jump, current, subroutine);
221                        newControlFlowEdge(insn, jump);
222                        for (int j = 0; j < lsi.labels.size(); ++j) {
223                            LabelNode label = lsi.labels.get(j);
224                            jump = insns.indexOf(label);
225                            merge(jump, current, subroutine);
226                            newControlFlowEdge(insn, jump);
227                        }
228                    } else if (insnNode instanceof TableSwitchInsnNode) {
229                        TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode;
230                        int jump = insns.indexOf(tsi.dflt);
231                        merge(jump, current, subroutine);
232                        newControlFlowEdge(insn, jump);
233                        for (int j = 0; j < tsi.labels.size(); ++j) {
234                            LabelNode label = tsi.labels.get(j);
235                            jump = insns.indexOf(label);
236                            merge(jump, current, subroutine);
237                            newControlFlowEdge(insn, jump);
238                        }
239                    } else if (insnOpcode == RET) {
240                        if (subroutine == null) {
241                            throw new AnalyzerException(insnNode,
242                                    "RET instruction outside of a sub routine");
243                        }
244                        for (int i = 0; i < subroutine.callers.size(); ++i) {
245                            JumpInsnNode caller = subroutine.callers.get(i);
246                            int call = insns.indexOf(caller);
247                            if (frames[call] != null) {
248                                merge(call + 1, frames[call], current,
249                                        subroutines[call], subroutine.access);
250                                newControlFlowEdge(insn, call + 1);
251                            }
252                        }
253                    } else if (insnOpcode != ATHROW
254                            && (insnOpcode < IRETURN || insnOpcode > RETURN)) {
255                        if (subroutine != null) {
256                            if (insnNode instanceof VarInsnNode) {
257                                int var = ((VarInsnNode) insnNode).var;
258                                subroutine.access[var] = true;
259                                if (insnOpcode == LLOAD || insnOpcode == DLOAD
260                                        || insnOpcode == LSTORE
261                                        || insnOpcode == DSTORE) {
262                                    subroutine.access[var + 1] = true;
263                                }
264                            } else if (insnNode instanceof IincInsnNode) {
265                                int var = ((IincInsnNode) insnNode).var;
266                                subroutine.access[var] = true;
267                            }
268                        }
269                        merge(insn + 1, current, subroutine);
270                        newControlFlowEdge(insn, insn + 1);
271                    }
272                }
273
274                List<TryCatchBlockNode> insnHandlers = handlers[insn];
275                if (insnHandlers != null) {
276                    for (int i = 0; i < insnHandlers.size(); ++i) {
277                        TryCatchBlockNode tcb = insnHandlers.get(i);
278                        Type type;
279                        if (tcb.type == null) {
280                            type = Type.getObjectType("java/lang/Throwable");
281                        } else {
282                            type = Type.getObjectType(tcb.type);
283                        }
284                        int jump = insns.indexOf(tcb.handler);
285                        if (newControlFlowExceptionEdge(insn, tcb)) {
286                            handler.init(f);
287                            handler.clearStack();
288                            handler.push(interpreter.newValue(type));
289                            merge(jump, handler, subroutine);
290                        }
291                    }
292                }
293            } catch (AnalyzerException e) {
294                throw new AnalyzerException(e.node, "Error at instruction "
295                        + insn + ": " + e.getMessage(), e);
296            } catch (Exception e) {
297                throw new AnalyzerException(insnNode, "Error at instruction "
298                        + insn + ": " + e.getMessage(), e);
299            }
300        }
301
302        return frames;
303    }
304
305    private void findSubroutine(int insn, final Subroutine sub,
306            final List<AbstractInsnNode> calls) throws AnalyzerException {
307        while (true) {
308            if (insn < 0 || insn >= n) {
309                throw new AnalyzerException(null,
310                        "Execution can fall off end of the code");
311            }
312            if (subroutines[insn] != null) {
313                return;
314            }
315            subroutines[insn] = sub.copy();
316            AbstractInsnNode node = insns.get(insn);
317
318            // calls findSubroutine recursively on normal successors
319            if (node instanceof JumpInsnNode) {
320                if (node.getOpcode() == JSR) {
321                    // do not follow a JSR, it leads to another subroutine!
322                    calls.add(node);
323                } else {
324                    JumpInsnNode jnode = (JumpInsnNode) node;
325                    findSubroutine(insns.indexOf(jnode.label), sub, calls);
326                }
327            } else if (node instanceof TableSwitchInsnNode) {
328                TableSwitchInsnNode tsnode = (TableSwitchInsnNode) node;
329                findSubroutine(insns.indexOf(tsnode.dflt), sub, calls);
330                for (int i = tsnode.labels.size() - 1; i >= 0; --i) {
331                    LabelNode l = tsnode.labels.get(i);
332                    findSubroutine(insns.indexOf(l), sub, calls);
333                }
334            } else if (node instanceof LookupSwitchInsnNode) {
335                LookupSwitchInsnNode lsnode = (LookupSwitchInsnNode) node;
336                findSubroutine(insns.indexOf(lsnode.dflt), sub, calls);
337                for (int i = lsnode.labels.size() - 1; i >= 0; --i) {
338                    LabelNode l = lsnode.labels.get(i);
339                    findSubroutine(insns.indexOf(l), sub, calls);
340                }
341            }
342
343            // calls findSubroutine recursively on exception handler successors
344            List<TryCatchBlockNode> insnHandlers = handlers[insn];
345            if (insnHandlers != null) {
346                for (int i = 0; i < insnHandlers.size(); ++i) {
347                    TryCatchBlockNode tcb = insnHandlers.get(i);
348                    findSubroutine(insns.indexOf(tcb.handler), sub, calls);
349                }
350            }
351
352            // if insn does not falls through to the next instruction, return.
353            switch (node.getOpcode()) {
354            case GOTO:
355            case RET:
356            case TABLESWITCH:
357            case LOOKUPSWITCH:
358            case IRETURN:
359            case LRETURN:
360            case FRETURN:
361            case DRETURN:
362            case ARETURN:
363            case RETURN:
364            case ATHROW:
365                return;
366            }
367            insn++;
368        }
369    }
370
371    /**
372     * Returns the symbolic stack frame for each instruction of the last
373     * recently analyzed method.
374     * 
375     * @return the symbolic state of the execution stack frame at each bytecode
376     *         instruction of the method. The size of the returned array is
377     *         equal to the number of instructions (and labels) of the method. A
378     *         given frame is <tt>null</tt> if the corresponding instruction
379     *         cannot be reached, or if an error occured during the analysis of
380     *         the method.
381     */
382    public Frame<V>[] getFrames() {
383        return frames;
384    }
385
386    /**
387     * Returns the exception handlers for the given instruction.
388     * 
389     * @param insn
390     *            the index of an instruction of the last recently analyzed
391     *            method.
392     * @return a list of {@link TryCatchBlockNode} objects.
393     */
394    public List<TryCatchBlockNode> getHandlers(final int insn) {
395        return handlers[insn];
396    }
397
398    /**
399     * Initializes this analyzer. This method is called just before the
400     * execution of control flow analysis loop in #analyze. The default
401     * implementation of this method does nothing.
402     * 
403     * @param owner
404     *            the internal name of the class to which the method belongs.
405     * @param m
406     *            the method to be analyzed.
407     * @throws AnalyzerException
408     *             if a problem occurs.
409     */
410    protected void init(String owner, MethodNode m) throws AnalyzerException {
411    }
412
413    /**
414     * Constructs a new frame with the given size.
415     * 
416     * @param nLocals
417     *            the maximum number of local variables of the frame.
418     * @param nStack
419     *            the maximum stack size of the frame.
420     * @return the created frame.
421     */
422    protected Frame<V> newFrame(final int nLocals, final int nStack) {
423        return new Frame<V>(nLocals, nStack);
424    }
425
426    /**
427     * Constructs a new frame that is identical to the given frame.
428     * 
429     * @param src
430     *            a frame.
431     * @return the created frame.
432     */
433    protected Frame<V> newFrame(final Frame<? extends V> src) {
434        return new Frame<V>(src);
435    }
436
437    /**
438     * Creates a control flow graph edge. The default implementation of this
439     * method does nothing. It can be overriden in order to construct the
440     * control flow graph of a method (this method is called by the
441     * {@link #analyze analyze} method during its visit of the method's code).
442     * 
443     * @param insn
444     *            an instruction index.
445     * @param successor
446     *            index of a successor instruction.
447     */
448    protected void newControlFlowEdge(final int insn, final int successor) {
449    }
450
451    /**
452     * Creates a control flow graph edge corresponding to an exception handler.
453     * The default implementation of this method does nothing. It can be
454     * overridden in order to construct the control flow graph of a method (this
455     * method is called by the {@link #analyze analyze} method during its visit
456     * of the method's code).
457     * 
458     * @param insn
459     *            an instruction index.
460     * @param successor
461     *            index of a successor instruction.
462     * @return true if this edge must be considered in the data flow analysis
463     *         performed by this analyzer, or false otherwise. The default
464     *         implementation of this method always returns true.
465     */
466    protected boolean newControlFlowExceptionEdge(final int insn,
467            final int successor) {
468        return true;
469    }
470
471    /**
472     * Creates a control flow graph edge corresponding to an exception handler.
473     * The default implementation of this method delegates to
474     * {@link #newControlFlowExceptionEdge(int, int)
475     * newControlFlowExceptionEdge(int, int)}. It can be overridden in order to
476     * construct the control flow graph of a method (this method is called by
477     * the {@link #analyze analyze} method during its visit of the method's
478     * code).
479     * 
480     * @param insn
481     *            an instruction index.
482     * @param tcb
483     *            TryCatchBlockNode corresponding to this edge.
484     * @return true if this edge must be considered in the data flow analysis
485     *         performed by this analyzer, or false otherwise. The default
486     *         implementation of this method delegates to
487     *         {@link #newControlFlowExceptionEdge(int, int)
488     *         newControlFlowExceptionEdge(int, int)}.
489     */
490    protected boolean newControlFlowExceptionEdge(final int insn,
491            final TryCatchBlockNode tcb) {
492        return newControlFlowExceptionEdge(insn, insns.indexOf(tcb.handler));
493    }
494
495    // -------------------------------------------------------------------------
496
497    private void merge(final int insn, final Frame<V> frame,
498            final Subroutine subroutine) throws AnalyzerException {
499        Frame<V> oldFrame = frames[insn];
500        Subroutine oldSubroutine = subroutines[insn];
501        boolean changes;
502
503        if (oldFrame == null) {
504            frames[insn] = newFrame(frame);
505            changes = true;
506        } else {
507            changes = oldFrame.merge(frame, interpreter);
508        }
509
510        if (oldSubroutine == null) {
511            if (subroutine != null) {
512                subroutines[insn] = subroutine.copy();
513                changes = true;
514            }
515        } else {
516            if (subroutine != null) {
517                changes |= oldSubroutine.merge(subroutine);
518            }
519        }
520        if (changes && !queued[insn]) {
521            queued[insn] = true;
522            queue[top++] = insn;
523        }
524    }
525
526    private void merge(final int insn, final Frame<V> beforeJSR,
527                       final Frame<V> afterRET, final Subroutine subroutineBeforeJSR,
528                       final boolean[] access) throws AnalyzerException {
529        Frame<V> oldFrame = frames[insn];
530        Subroutine oldSubroutine = subroutines[insn];
531        boolean changes;
532
533        afterRET.merge(beforeJSR, access);
534
535        if (oldFrame == null) {
536            frames[insn] = newFrame(afterRET);
537            changes = true;
538        } else {
539            changes = oldFrame.merge(afterRET, interpreter);
540        }
541
542        if (oldSubroutine != null && subroutineBeforeJSR != null) {
543            changes |= oldSubroutine.merge(subroutineBeforeJSR);
544        }
545        if (changes && !queued[insn]) {
546            queued[insn] = true;
547            queue[top++] = insn;
548        }
549    }
550}