aboutsummaryrefslogtreecommitdiff
path: root/src/main/javassist/bytecode/stackmap/MapMaker.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/javassist/bytecode/stackmap/MapMaker.java')
-rw-r--r--src/main/javassist/bytecode/stackmap/MapMaker.java370
1 files changed, 228 insertions, 142 deletions
diff --git a/src/main/javassist/bytecode/stackmap/MapMaker.java b/src/main/javassist/bytecode/stackmap/MapMaker.java
index 92cd37c..bd79377 100644
--- a/src/main/javassist/bytecode/stackmap/MapMaker.java
+++ b/src/main/javassist/bytecode/stackmap/MapMaker.java
@@ -1,11 +1,12 @@
/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ * 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.
+ * 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
@@ -15,8 +16,19 @@
package javassist.bytecode.stackmap;
+import java.util.ArrayList;
+import java.util.List;
+
import javassist.ClassPool;
-import javassist.bytecode.*;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.ByteArray;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.StackMap;
+import javassist.bytecode.StackMapTable;
/**
* Stack map maker.
@@ -79,7 +91,7 @@ public class MapMaker extends Tracer {
/**
* Computes the stack map table of the given method and returns it.
* It returns null if the given method does not have to have a
- * stack map table.
+ * stack map table or it includes JSR.
*/
public static StackMapTable make(ClassPool classes, MethodInfo minfo)
throws BadBytecode
@@ -88,19 +100,32 @@ public class MapMaker extends Tracer {
if (ca == null)
return null;
- TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, true);
+ TypedBlock[] blocks;
+ try {
+ blocks = TypedBlock.makeBlocks(minfo, ca, true);
+ }
+ catch (BasicBlock.JsrBytecode e) {
+ return null;
+ }
+
if (blocks == null)
return null;
MapMaker mm = new MapMaker(classes, minfo, ca);
- mm.make(blocks, ca.getCode());
+ try {
+ mm.make(blocks, ca.getCode());
+ }
+ catch (BadBytecode bb) {
+ throw new BadBytecode(minfo, bb);
+ }
+
return mm.toStackMap(blocks);
}
/**
* Computes the stack map table for J2ME.
* It returns null if the given method does not have to have a
- * stack map table.
+ * stack map table or it includes JSR.
*/
public static StackMap make2(ClassPool classes, MethodInfo minfo)
throws BadBytecode
@@ -109,12 +134,24 @@ public class MapMaker extends Tracer {
if (ca == null)
return null;
- TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, true);
+ TypedBlock[] blocks;
+ try {
+ blocks = TypedBlock.makeBlocks(minfo, ca, true);
+ }
+ catch (BasicBlock.JsrBytecode e) {
+ return null;
+ }
+
if (blocks == null)
return null;
MapMaker mm = new MapMaker(classes, minfo, ca);
- mm.make(blocks, ca.getCode());
+ try {
+ mm.make(blocks, ca.getCode());
+ }
+ catch (BadBytecode bb) {
+ throw new BadBytecode(minfo, bb);
+ }
return mm.toStackMap2(minfo.getConstPool(), blocks);
}
@@ -124,9 +161,7 @@ public class MapMaker extends Tracer {
TypedBlock.getRetType(minfo.getDescriptor()));
}
- protected MapMaker(MapMaker old, boolean copyStack) {
- super(old, copyStack);
- }
+ protected MapMaker(MapMaker old) { super(old); }
/**
* Runs an analyzer (Phase 1 and 2).
@@ -134,34 +169,12 @@ public class MapMaker extends Tracer {
void make(TypedBlock[] blocks, byte[] code)
throws BadBytecode
{
- TypedBlock first = blocks[0];
- fixParamTypes(first);
- TypeData[] srcTypes = first.localsTypes;
- copyFrom(srcTypes.length, srcTypes, this.localsTypes);
- make(code, first);
-
- int n = blocks.length;
- for (int i = 0; i < n; i++)
- evalExpected(blocks[i]);
- }
-
- /*
- * If a parameter type is String but it is used only as Object
- * within the method body, this MapMaker class will report its type
- * is Object. To avoid this, fixParamTypes calls TypeData.setType()
- * on each parameter type.
- */
- private void fixParamTypes(TypedBlock first) throws BadBytecode {
- TypeData[] types = first.localsTypes;
- int n = types.length;
- for (int i = 0; i < n; i++) {
- TypeData t = types[i];
- if (t instanceof TypeData.ClassName) {
- /* Skip the following statement if t.isNullType() is true
- * although a parameter type is never null type.
- */
- TypeData.setType(t, t.getName(), classPool);
- }
+ make(code, blocks[0]);
+ findDeadCatchers(code, blocks);
+ try {
+ fixTypes(code, blocks);
+ } catch (NotFoundException e) {
+ throw new BadBytecode("failed to resolve types", e);
}
}
@@ -170,16 +183,18 @@ public class MapMaker extends Tracer {
private void make(byte[] code, TypedBlock tb)
throws BadBytecode
{
- BasicBlock.Catch handlers = tb.toCatch;
- while (handlers != null) {
- traceException(code, handlers);
- handlers = handlers.next;
- }
+ copyTypeData(tb.stackTop, tb.stackTypes, stackTypes);
+ stackTop = tb.stackTop;
+ copyTypeData(tb.localsTypes.length, tb.localsTypes, localsTypes);
+
+ traceException(code, tb.toCatch);
int pos = tb.position;
int end = pos + tb.length;
- while (pos < end)
+ while (pos < end) {
pos += doOpcode(pos, code);
+ traceException(code, tb.toCatch);
+ }
if (tb.exit != null) {
for (int i = 0; i < tb.exit.length; i++) {
@@ -188,7 +203,7 @@ public class MapMaker extends Tracer {
mergeMap(e, true);
else {
recordStackMap(e);
- MapMaker maker = new MapMaker(this, true);
+ MapMaker maker = new MapMaker(this);
maker.make(code, e);
}
}
@@ -198,106 +213,194 @@ public class MapMaker extends Tracer {
private void traceException(byte[] code, TypedBlock.Catch handler)
throws BadBytecode
{
- TypedBlock tb = (TypedBlock)handler.body;
- if (tb.alreadySet())
- mergeMap(tb, false);
- else {
- recordStackMap(tb, handler.typeIndex);
- MapMaker maker = new MapMaker(this, false);
-
- /* the following code is equivalent to maker.copyFrom(this)
- * except stackTypes are not copied.
- */
- maker.stackTypes[0] = tb.stackTypes[0].getSelf();
- maker.stackTop = 1;
- maker.make(code, tb);
+ while (handler != null) {
+ TypedBlock tb = (TypedBlock)handler.body;
+ if (tb.alreadySet()) {
+ mergeMap(tb, false);
+ if (tb.stackTop < 1)
+ throw new BadBytecode("bad catch clause: " + handler.typeIndex);
+
+ tb.stackTypes[0] = merge(toExceptionType(handler.typeIndex),
+ tb.stackTypes[0]);
+ }
+ else {
+ recordStackMap(tb, handler.typeIndex);
+ MapMaker maker = new MapMaker(this);
+ maker.make(code, tb);
+ }
+
+ handler = handler.next;
}
}
- private void mergeMap(TypedBlock dest, boolean mergeStack) {
- boolean[] inputs = dest.inputs;
- int n = inputs.length;
+ private void mergeMap(TypedBlock dest, boolean mergeStack) throws BadBytecode {
+ int n = localsTypes.length;
for (int i = 0; i < n; i++)
- if (inputs[i])
- merge(localsTypes[i], dest.localsTypes[i]);
+ dest.localsTypes[i] = merge(validateTypeData(localsTypes, n, i),
+ dest.localsTypes[i]);
if (mergeStack) {
n = stackTop;
for (int i = 0; i < n; i++)
- merge(stackTypes[i], dest.stackTypes[i]);
+ dest.stackTypes[i] = merge(stackTypes[i], dest.stackTypes[i]);
}
}
- private void merge(TypeData td, TypeData target) {
- boolean tdIsObj = false;
- boolean targetIsObj = false;
- // td or target is null if it is TOP.
- if (td != TOP && td.isObjectType())
- tdIsObj = true;
-
- if (target != TOP && target.isObjectType())
- targetIsObj = true;
-
- if (tdIsObj && targetIsObj)
- target.merge(td);
+ private TypeData merge(TypeData src, TypeData target) throws BadBytecode {
+ if (src == target)
+ return target;
+ else if (target instanceof TypeData.ClassName
+ || target instanceof TypeData.BasicType) // a parameter
+ return target;
+ else if (target instanceof TypeData.AbsTypeVar) {
+ ((TypeData.AbsTypeVar)target).merge(src);
+ return target;
+ }
+ else
+ throw new RuntimeException("fatal: this should never happen");
}
private void recordStackMap(TypedBlock target)
throws BadBytecode
{
- TypeData[] tStackTypes = new TypeData[stackTypes.length];
+ TypeData[] tStackTypes = TypeData.make(stackTypes.length);
int st = stackTop;
- copyFrom(st, stackTypes, tStackTypes);
+ recordTypeData(st, stackTypes, tStackTypes);
recordStackMap0(target, st, tStackTypes);
}
private void recordStackMap(TypedBlock target, int exceptionType)
throws BadBytecode
{
+ TypeData[] tStackTypes = TypeData.make(stackTypes.length);
+ tStackTypes[0] = toExceptionType(exceptionType).join();
+ recordStackMap0(target, 1, tStackTypes);
+ }
+
+ private TypeData.ClassName toExceptionType(int exceptionType) {
String type;
- if (exceptionType == 0)
- type = "java.lang.Throwable";
+ if (exceptionType == 0) // for finally clauses
+ type= "java.lang.Throwable";
else
type = cpool.getClassInfo(exceptionType);
- TypeData[] tStackTypes = new TypeData[stackTypes.length];
- tStackTypes[0] = new TypeData.ClassName(type);
-
- recordStackMap0(target, 1, tStackTypes);
+ return new TypeData.ClassName(type);
}
private void recordStackMap0(TypedBlock target, int st, TypeData[] tStackTypes)
throws BadBytecode
{
int n = localsTypes.length;
- TypeData[] tLocalsTypes = new TypeData[n];
- int k = copyFrom(n, localsTypes, tLocalsTypes);
+ TypeData[] tLocalsTypes = TypeData.make(n);
+ int k = recordTypeData(n, localsTypes, tLocalsTypes);
+ target.setStackMap(st, tStackTypes, k, tLocalsTypes);
+ }
+
+ protected static int recordTypeData(int n, TypeData[] srcTypes, TypeData[] destTypes) {
+ int k = -1;
+ for (int i = 0; i < n; i++) {
+ TypeData t = validateTypeData(srcTypes, n, i);
+ destTypes[i] = t.join();
+ if (t != TOP)
+ k = i + 1; // t might be long or double.
+ }
+
+ return k + 1;
+ }
- boolean[] inputs = target.inputs;
+ protected static void copyTypeData(int n, TypeData[] srcTypes, TypeData[] destTypes) {
for (int i = 0; i < n; i++)
- if (!inputs[i])
- tLocalsTypes[i] = TOP;
+ destTypes[i] = srcTypes[i];
+ }
- target.setStackMap(st, tStackTypes, k, tLocalsTypes);
+ private static TypeData validateTypeData(TypeData[] data, int length, int index) {
+ TypeData td = data[index];
+ if (td.is2WordType() && index + 1 < length)
+ if (data[index + 1] != TOP)
+ return TOP;
+
+ return td;
}
- // Phase 2
+ // Phase 1.5
+
+ /*
+ * Javac may generate an exception handler that catches only the exception
+ * thrown within the handler itself. It is dead code.
+ * See javassist.JvstTest4.testJIRA195().
+ */
- void evalExpected(TypedBlock target) throws BadBytecode {
- ClassPool cp = classPool;
- evalExpected(cp, target.stackTop, target.stackTypes);
- TypeData[] types = target.localsTypes;
- if (types != null) // unless this block is dead code
- evalExpected(cp, types.length, types);
+ private void findDeadCatchers(byte[] code, TypedBlock[] blocks) throws BadBytecode {
+ int len = blocks.length;
+ for (int i = 0; i < len; i++) {
+ TypedBlock block = blocks[i];
+ if (!block.alreadySet()) {
+ fixDeadcode(code, block);
+ BasicBlock.Catch handler = block.toCatch;
+ if (handler != null) {
+ TypedBlock tb = (TypedBlock)handler.body;
+ if (!tb.alreadySet()) {
+ // tb is a handler that catches only the exceptions
+ // thrown from dead code.
+ recordStackMap(tb, handler.typeIndex);
+ fixDeadcode(code, tb);
+ tb.incoming = 1;
+ }
+ }
+
+ }
+ }
}
- private static void evalExpected(ClassPool cp, int n, TypeData[] types)
- throws BadBytecode
- {
- for (int i = 0; i < n; i++) {
- TypeData td = types[i];
- if (td != null)
- td.evalExpectedType(cp);
+ private void fixDeadcode(byte[] code, TypedBlock block) throws BadBytecode {
+ int pos = block.position;
+ int len = block.length - 3;
+ if (len < 0) {
+ // if the dead-code length is shorter than 3 bytes.
+ if (len == -1)
+ code[pos] = Bytecode.NOP;
+
+ code[pos + block.length - 1] = (byte)Bytecode.ATHROW;
+ block.incoming = 1;
+ recordStackMap(block, 0);
+ return;
+ }
+
+ // if block.incomping > 0, all the incoming edges are from
+ // other dead code blocks. So set block.incoming to 0.
+ block.incoming = 0;
+
+ for (int k = 0; k < len; k++)
+ code[pos + k] = Bytecode.NOP;
+
+ code[pos + len] = (byte)Bytecode.GOTO;
+ ByteArray.write16bit(-len, code, pos + len + 1);
+ }
+
+ // Phase 2
+
+ /*
+ * This method first finds strongly connected components (SCCs)
+ * in a TypeData graph by Tarjan's algorithm.
+ * SCCs are TypeData nodes sharing the same type.
+ * Since SCCs are found in the topologically sorted order,
+ * their types are also fixed when they are found.
+ */
+ private void fixTypes(byte[] code, TypedBlock[] blocks) throws NotFoundException, BadBytecode {
+ List<TypeData> preOrder = new ArrayList<TypeData>();
+ int len = blocks.length;
+ int index = 0;
+ for (int i = 0; i < len; i++) {
+ TypedBlock block = blocks[i];
+ if (block.alreadySet()) { // if block is not dead code
+ int n = block.localsTypes.length;
+ for (int j = 0; j < n; j++)
+ index = block.localsTypes[j].dfs(preOrder, index, classPool);
+
+ n = block.stackTop;
+ for (int j = 0; j < n; j++)
+ index = block.stackTypes[j].dfs(preOrder, index, classPool);
+ }
}
}
@@ -323,6 +426,11 @@ public class MapMaker extends Tracer {
offsetDelta = bb.length - 1;
prev = bb;
}
+ else if (bb.incoming == 0) {
+ // dead code.
+ writer.sameFrame(offsetDelta);
+ offsetDelta = bb.length - 1;
+ }
else
offsetDelta += bb.length;
}
@@ -369,19 +477,14 @@ public class MapMaker extends Tracer {
}
else if (stackTop == 1 && diffL == 0) {
TypeData td = bb.stackTypes[0];
- if (td == TOP)
- writer.sameLocals(offsetDelta, StackMapTable.TOP, 0);
- else
- writer.sameLocals(offsetDelta, td.getTypeTag(),
- td.getTypeData(cpool));
+ writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool));
return;
}
else if (stackTop == 2 && diffL == 0) {
TypeData td = bb.stackTypes[0];
- if (td != TOP && td.is2WordType()) {
+ if (td.is2WordType()) {
// bb.stackTypes[1] must be TOP.
- writer.sameLocals(offsetDelta, td.getTypeTag(),
- td.getTypeData(cpool));
+ writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool));
return;
}
}
@@ -400,16 +503,10 @@ public class MapMaker extends Tracer {
int j = 0;
for (int i = 0; i < num; i++) {
TypeData td = types[offset + i];
- if (td == TOP) {
- tags[j] = StackMapTable.TOP;
- data[j] = 0;
- }
- else {
- tags[j] = td.getTypeTag();
- data[j] = td.getTypeData(cp);
- if (td.is2WordType())
- i++;
- }
+ tags[j] = td.getTypeTag();
+ data[j] = td.getTypeData(cp);
+ if (td.is2WordType())
+ i++;
j++;
}
@@ -432,20 +529,13 @@ public class MapMaker extends Tracer {
return diffSize(newTd, len, newTdLen);
else
return -diffSize(oldTd, len, oldTdLen);
- else
- return -100;
+ return -100;
}
private static boolean stackMapEq(TypeData[] oldTd, TypeData[] newTd, int len) {
for (int i = 0; i < len; i++) {
- TypeData td = oldTd[i];
- if (td == TOP) { // the next element to LONG/DOUBLE is TOP.
- if (newTd[i] != TOP)
- return false;
- }
- else
- if (!oldTd[i].equals(newTd[i]))
- return false;
+ if (!oldTd[i].eq(newTd[i]))
+ return false;
}
return true;
@@ -456,7 +546,7 @@ public class MapMaker extends Tracer {
while (offset < len) {
TypeData td = types[offset++];
num++;
- if (td != TOP && td.is2WordType())
+ if (td.is2WordType())
offset++;
}
@@ -514,13 +604,9 @@ public class MapMaker extends Tracer {
writer.write16bit(num - numDWord);
for (int i = 0; i < num; i++) {
TypeData td = types[i];
- if (td == TOP)
- writer.writeVerifyTypeInfo(StackMap.TOP, 0);
- else {
- writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp));
- if (td.is2WordType())
- i++;
- }
+ writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp));
+ if (td.is2WordType())
+ i++;
}
}
}