diff options
Diffstat (limited to 'src/main/javassist/bytecode/stackmap/MapMaker.java')
-rw-r--r-- | src/main/javassist/bytecode/stackmap/MapMaker.java | 370 |
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++; } } } |