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}