Tue, 06 Aug 2013 15:08:49 +0100
8022186: javac generates dead code if a try with an empty body has a finalizer
Reviewed-by: jjg
1.1 --- a/src/share/classes/com/sun/tools/javac/jvm/Gen.java Wed Jul 31 08:37:34 2013 -0700 1.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/Gen.java Tue Aug 06 15:08:49 2013 +0100 1.3 @@ -1478,74 +1478,82 @@ 1.4 code.statBegin(TreeInfo.endPos(body)); 1.5 genFinalizer(env); 1.6 code.statBegin(TreeInfo.endPos(env.tree)); 1.7 - Chain exitChain = code.branch(goto_); 1.8 + Chain exitChain; 1.9 + if (startpc != endpc) { 1.10 + exitChain = code.branch(goto_); 1.11 + } else { 1.12 + exitChain = code.branch(dontgoto); 1.13 + } 1.14 endFinalizerGap(env); 1.15 - if (startpc != endpc) for (List<JCCatch> l = catchers; l.nonEmpty(); l = l.tail) { 1.16 - // start off with exception on stack 1.17 - code.entryPoint(stateTry, l.head.param.sym.type); 1.18 - genCatch(l.head, env, startpc, endpc, gaps); 1.19 - genFinalizer(env); 1.20 - if (hasFinalizer || l.tail.nonEmpty()) { 1.21 - code.statBegin(TreeInfo.endPos(env.tree)); 1.22 - exitChain = Code.mergeChains(exitChain, 1.23 - code.branch(goto_)); 1.24 + if (startpc != endpc) { 1.25 + for (List<JCCatch> l = catchers; l.nonEmpty(); l = l.tail) { 1.26 + // start off with exception on stack 1.27 + code.entryPoint(stateTry, l.head.param.sym.type); 1.28 + genCatch(l.head, env, startpc, endpc, gaps); 1.29 + genFinalizer(env); 1.30 + if (hasFinalizer || l.tail.nonEmpty()) { 1.31 + code.statBegin(TreeInfo.endPos(env.tree)); 1.32 + exitChain = Code.mergeChains(exitChain, 1.33 + code.branch(goto_)); 1.34 + } 1.35 + endFinalizerGap(env); 1.36 } 1.37 - endFinalizerGap(env); 1.38 - } 1.39 - if (hasFinalizer) { 1.40 - // Create a new register segement to avoid allocating 1.41 - // the same variables in finalizers and other statements. 1.42 - code.newRegSegment(); 1.43 1.44 - // Add a catch-all clause. 1.45 + if (hasFinalizer) { 1.46 + // Create a new register segement to avoid allocating 1.47 + // the same variables in finalizers and other statements. 1.48 + code.newRegSegment(); 1.49 1.50 - // start off with exception on stack 1.51 - int catchallpc = code.entryPoint(stateTry, syms.throwableType); 1.52 + // Add a catch-all clause. 1.53 1.54 - // Register all exception ranges for catch all clause. 1.55 - // The range of the catch all clause is from the beginning 1.56 - // of the try or synchronized block until the present 1.57 - // code pointer excluding all gaps in the current 1.58 - // environment's GenContext. 1.59 - int startseg = startpc; 1.60 - while (env.info.gaps.nonEmpty()) { 1.61 - int endseg = env.info.gaps.next().intValue(); 1.62 - registerCatch(body.pos(), startseg, endseg, 1.63 - catchallpc, 0); 1.64 - startseg = env.info.gaps.next().intValue(); 1.65 - } 1.66 - code.statBegin(TreeInfo.finalizerPos(env.tree)); 1.67 - code.markStatBegin(); 1.68 + // start off with exception on stack 1.69 + int catchallpc = code.entryPoint(stateTry, syms.throwableType); 1.70 1.71 - Item excVar = makeTemp(syms.throwableType); 1.72 - excVar.store(); 1.73 - genFinalizer(env); 1.74 - excVar.load(); 1.75 - registerCatch(body.pos(), startseg, 1.76 - env.info.gaps.next().intValue(), 1.77 - catchallpc, 0); 1.78 - code.emitop0(athrow); 1.79 - code.markDead(); 1.80 - 1.81 - // If there are jsr's to this finalizer, ... 1.82 - if (env.info.cont != null) { 1.83 - // Resolve all jsr's. 1.84 - code.resolve(env.info.cont); 1.85 - 1.86 - // Mark statement line number 1.87 + // Register all exception ranges for catch all clause. 1.88 + // The range of the catch all clause is from the beginning 1.89 + // of the try or synchronized block until the present 1.90 + // code pointer excluding all gaps in the current 1.91 + // environment's GenContext. 1.92 + int startseg = startpc; 1.93 + while (env.info.gaps.nonEmpty()) { 1.94 + int endseg = env.info.gaps.next().intValue(); 1.95 + registerCatch(body.pos(), startseg, endseg, 1.96 + catchallpc, 0); 1.97 + startseg = env.info.gaps.next().intValue(); 1.98 + } 1.99 code.statBegin(TreeInfo.finalizerPos(env.tree)); 1.100 code.markStatBegin(); 1.101 1.102 - // Save return address. 1.103 - LocalItem retVar = makeTemp(syms.throwableType); 1.104 - retVar.store(); 1.105 + Item excVar = makeTemp(syms.throwableType); 1.106 + excVar.store(); 1.107 + genFinalizer(env); 1.108 + excVar.load(); 1.109 + registerCatch(body.pos(), startseg, 1.110 + env.info.gaps.next().intValue(), 1.111 + catchallpc, 0); 1.112 + code.emitop0(athrow); 1.113 + code.markDead(); 1.114 1.115 - // Generate finalizer code. 1.116 - env.info.finalize.genLast(); 1.117 + // If there are jsr's to this finalizer, ... 1.118 + if (env.info.cont != null) { 1.119 + // Resolve all jsr's. 1.120 + code.resolve(env.info.cont); 1.121 1.122 - // Return. 1.123 - code.emitop1w(ret, retVar.reg); 1.124 - code.markDead(); 1.125 + // Mark statement line number 1.126 + code.statBegin(TreeInfo.finalizerPos(env.tree)); 1.127 + code.markStatBegin(); 1.128 + 1.129 + // Save return address. 1.130 + LocalItem retVar = makeTemp(syms.throwableType); 1.131 + retVar.store(); 1.132 + 1.133 + // Generate finalizer code. 1.134 + env.info.finalize.genLast(); 1.135 + 1.136 + // Return. 1.137 + code.emitop1w(ret, retVar.reg); 1.138 + code.markDead(); 1.139 + } 1.140 } 1.141 } 1.142 // Resolve all breaks.
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/tools/javac/T8022186/DeadCodeGeneratedForEmptyTryTest.java Tue Aug 06 15:08:49 2013 +0100 2.3 @@ -0,0 +1,166 @@ 2.4 +/* 2.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.7 + * 2.8 + * This code is free software; you can redistribute it and/or modify it 2.9 + * under the terms of the GNU General Public License version 2 only, as 2.10 + * published by the Free Software Foundation. Oracle designates this 2.11 + * particular file as subject to the "Classpath" exception as provided 2.12 + * by Oracle in the LICENSE file that accompanied this code. 2.13 + * 2.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 2.17 + * version 2 for more details (a copy is included in the LICENSE file that 2.18 + * accompanied this code). 2.19 + * 2.20 + * You should have received a copy of the GNU General Public License version 2.21 + * 2 along with this work; if not, write to the Free Software Foundation, 2.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.23 + * 2.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2.25 + * or visit www.oracle.com if you need additional information or have any 2.26 + * questions. 2.27 + */ 2.28 + 2.29 +/* 2.30 + * @test 2.31 + * @bug 8022186 2.32 + * @summary javac generates dead code if a try with an empty body has a finalizer 2.33 + */ 2.34 + 2.35 +import com.sun.tools.classfile.Attribute; 2.36 +import com.sun.tools.classfile.ClassFile; 2.37 +import com.sun.tools.classfile.Code_attribute; 2.38 +import com.sun.tools.classfile.ConstantPool; 2.39 +import com.sun.tools.classfile.ConstantPool.CONSTANT_String_info; 2.40 +import com.sun.tools.classfile.ConstantPool.CPInfo; 2.41 +import com.sun.tools.classfile.ConstantPool.InvalidIndex; 2.42 +import com.sun.tools.classfile.Instruction; 2.43 +import com.sun.tools.classfile.Instruction.KindVisitor; 2.44 +import com.sun.tools.classfile.Instruction.TypeKind; 2.45 +import com.sun.tools.classfile.Method; 2.46 +import com.sun.tools.javac.util.Assert; 2.47 +import java.io.BufferedInputStream; 2.48 +import java.nio.file.Files; 2.49 +import java.nio.file.Path; 2.50 +import java.nio.file.Paths; 2.51 + 2.52 +public class DeadCodeGeneratedForEmptyTryTest { 2.53 + 2.54 + public static void main(String[] args) throws Exception { 2.55 + new DeadCodeGeneratedForEmptyTryTest().run(); 2.56 + } 2.57 + 2.58 + void run() throws Exception { 2.59 + checkClassFile(Paths.get(System.getProperty("test.classes"), 2.60 + this.getClass().getName() + "$Test.class")); 2.61 + } 2.62 + 2.63 + int utf8Index; 2.64 + int numberOfRefToStr = 0; 2.65 + ConstantPool constantPool; 2.66 + 2.67 + void checkClassFile(final Path path) throws Exception { 2.68 + ClassFile classFile = ClassFile.read( 2.69 + new BufferedInputStream(Files.newInputStream(path))); 2.70 + constantPool = classFile.constant_pool; 2.71 + utf8Index = constantPool.getUTF8Index("STR_TO_LOOK_FOR"); 2.72 + for (Method method: classFile.methods) { 2.73 + if (method.getName(constantPool).equals("methodToLookFor")) { 2.74 + Code_attribute codeAtt = (Code_attribute)method.attributes.get(Attribute.Code); 2.75 + for (Instruction inst: codeAtt.getInstructions()) { 2.76 + inst.accept(codeVisitor, null); 2.77 + } 2.78 + } 2.79 + } 2.80 + Assert.check(numberOfRefToStr == 1, 2.81 + "There should only be one reference to a CONSTANT_String_info structure in the generated code"); 2.82 + } 2.83 + 2.84 + CodeVisitor codeVisitor = new CodeVisitor(); 2.85 + 2.86 + class CodeVisitor implements KindVisitor<Void, Void> { 2.87 + 2.88 + void checkIndirectRefToString(int cp_index) { 2.89 + try { 2.90 + CPInfo cInfo = constantPool.get(cp_index); 2.91 + if (cInfo instanceof CONSTANT_String_info) { 2.92 + CONSTANT_String_info strInfo = (CONSTANT_String_info)cInfo; 2.93 + if (strInfo.string_index == utf8Index) { 2.94 + numberOfRefToStr++; 2.95 + } 2.96 + } 2.97 + } catch (InvalidIndex ex) { 2.98 + throw new AssertionError("invalid constant pool index at " + cp_index); 2.99 + } 2.100 + } 2.101 + 2.102 + @Override 2.103 + public Void visitNoOperands(Instruction instr, Void p) { 2.104 + return null; 2.105 + } 2.106 + 2.107 + @Override 2.108 + public Void visitArrayType(Instruction instr, TypeKind kind, Void p) { 2.109 + return null; 2.110 + } 2.111 + 2.112 + @Override 2.113 + public Void visitBranch(Instruction instr, int offset, Void p) { 2.114 + return null; 2.115 + } 2.116 + 2.117 + @Override 2.118 + public Void visitConstantPoolRef(Instruction instr, int index, Void p) { 2.119 + checkIndirectRefToString(index); 2.120 + return null; 2.121 + } 2.122 + 2.123 + @Override 2.124 + public Void visitConstantPoolRefAndValue(Instruction instr, int index, int value, Void p) { 2.125 + checkIndirectRefToString(index); 2.126 + return null; 2.127 + } 2.128 + 2.129 + @Override 2.130 + public Void visitLocal(Instruction instr, int index, Void p) { 2.131 + return null; 2.132 + } 2.133 + 2.134 + @Override 2.135 + public Void visitLocalAndValue(Instruction instr, int index, int value, Void p) { 2.136 + return null; 2.137 + } 2.138 + 2.139 + @Override 2.140 + public Void visitLookupSwitch(Instruction instr, int default_, int npairs, int[] matches, int[] offsets, Void p) { 2.141 + return null; 2.142 + } 2.143 + 2.144 + @Override 2.145 + public Void visitTableSwitch(Instruction instr, int default_, int low, int high, int[] offsets, Void p) { 2.146 + return null; 2.147 + } 2.148 + 2.149 + @Override 2.150 + public Void visitValue(Instruction instr, int value, Void p) { 2.151 + return null; 2.152 + } 2.153 + 2.154 + @Override 2.155 + public Void visitUnknown(Instruction instr, Void p) { 2.156 + return null; 2.157 + } 2.158 + 2.159 + } 2.160 + 2.161 + public class Test { 2.162 + void methodToLookFor() { 2.163 + try { 2.164 + } finally { 2.165 + System.out.println("STR_TO_LOOK_FOR"); 2.166 + } 2.167 + } 2.168 + } 2.169 +}