/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.bytecode.analysis;
import java.util.
HashMap;
import java.util.
HashSet;
import java.util.
Map;
import java.util.
Set;
import javassist.bytecode.
BadBytecode;
import javassist.bytecode.
CodeAttribute;
import javassist.bytecode.
CodeIterator;
import javassist.bytecode.
ExceptionTable;
import javassist.bytecode.
MethodInfo;
import javassist.bytecode.
Opcode;
/**
* Discovers the subroutines in a method, and tracks all callers.
*
* @author Jason T. Greene
*/
public class
SubroutineScanner implements
Opcode {
private
Subroutine[]
subroutines;
Map subTable = new
HashMap();
Set done = new
HashSet();
public
Subroutine[]
scan(
MethodInfo method) throws
BadBytecode {
CodeAttribute code =
method.
getCodeAttribute();
CodeIterator iter =
code.
iterator();
subroutines = new
Subroutine[
code.
getCodeLength()];
subTable.
clear();
done.
clear();
scan(0,
iter, null);
ExceptionTable exceptions =
code.
getExceptionTable();
for (int
i = 0;
i <
exceptions.
size();
i++) {
int
handler =
exceptions.
handlerPc(
i);
// If an exception is thrown in subroutine, the handler
// is part of the same subroutine.
scan(
handler,
iter,
subroutines[
exceptions.
startPc(
i)]);
}
return
subroutines;
}
private void
scan(int
pos,
CodeIterator iter,
Subroutine sub) throws
BadBytecode {
// Skip already processed blocks
if (
done.
contains(new
Integer(
pos)))
return;
done.
add(new
Integer(
pos));
int
old =
iter.
lookAhead();
iter.
move(
pos);
boolean
next;
do {
pos =
iter.
next();
next =
scanOp(
pos,
iter,
sub) &&
iter.
hasNext();
} while (
next);
iter.
move(
old);
}
private boolean
scanOp(int
pos,
CodeIterator iter,
Subroutine sub) throws
BadBytecode {
subroutines[
pos] =
sub;
int
opcode =
iter.
byteAt(
pos);
if (
opcode ==
TABLESWITCH) {
scanTableSwitch(
pos,
iter,
sub);
return false;
}
if (
opcode ==
LOOKUPSWITCH) {
scanLookupSwitch(
pos,
iter,
sub);
return false;
}
// All forms of return and throw end current code flow
if (
Util.
isReturn(
opcode) ||
opcode ==
RET ||
opcode ==
ATHROW)
return false;
if (
Util.
isJumpInstruction(
opcode)) {
int
target =
Util.
getJumpTarget(
pos,
iter);
if (
opcode ==
JSR ||
opcode ==
JSR_W) {
Subroutine s = (
Subroutine)
subTable.
get(new
Integer(
target));
if (
s == null) {
s = new
Subroutine(
target,
pos);
subTable.
put(new
Integer(
target),
s);
scan(
target,
iter,
s);
} else {
s.
addCaller(
pos);
}
} else {
scan(
target,
iter,
sub);
// GOTO ends current code flow
if (
Util.
isGoto(
opcode))
return false;
}
}
return true;
}
private void
scanLookupSwitch(int
pos,
CodeIterator iter,
Subroutine sub) throws
BadBytecode {
int
index = (
pos & ~3) + 4;
// default
scan(
pos +
iter.
s32bitAt(
index),
iter,
sub);
int
npairs =
iter.
s32bitAt(
index += 4);
int
end =
npairs * 8 + (
index += 4);
// skip "match"
for (
index += 4;
index <
end;
index += 8) {
int
target =
iter.
s32bitAt(
index) +
pos;
scan(
target,
iter,
sub);
}
}
private void
scanTableSwitch(int
pos,
CodeIterator iter,
Subroutine sub) throws
BadBytecode {
// Skip 4 byte alignment padding
int
index = (
pos & ~3) + 4;
// default
scan(
pos +
iter.
s32bitAt(
index),
iter,
sub);
int
low =
iter.
s32bitAt(
index += 4);
int
high =
iter.
s32bitAt(
index += 4);
int
end = (
high -
low + 1) * 4 + (
index += 4);
// Offset table
for (;
index <
end;
index += 4) {
int
target =
iter.
s32bitAt(
index) +
pos;
scan(
target,
iter,
sub);
}
}
}