langtools/test/tools/javac/flow/LVTHarness.java
changeset 19941 8b91e8eb2d20
child 20248 0c72984b7a4d
equal deleted inserted replaced
19940:d6d6e623f0b4 19941:8b91e8eb2d20
       
     1 /*
       
     2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 /*
       
    25  * @test
       
    26  * @bug 7047734
       
    27  * @summary The LVT is not generated correctly during some try/catch scenarios
       
    28  * @library /tools/javac/lib
       
    29  * @build JavacTestingAbstractProcessor LVTHarness
       
    30  * @run main LVTHarness
       
    31  */
       
    32 
       
    33 import java.io.File;
       
    34 import java.io.IOException;
       
    35 import java.lang.annotation.Annotation;
       
    36 import java.util.Set;
       
    37 import java.util.Arrays;
       
    38 import java.util.ArrayList;
       
    39 import java.util.Collections;
       
    40 import java.util.HashMap;
       
    41 import java.util.HashSet;
       
    42 import java.util.List;
       
    43 import java.util.Map;
       
    44 
       
    45 import javax.annotation.processing.RoundEnvironment;
       
    46 import javax.lang.model.element.Element;
       
    47 import javax.lang.model.element.TypeElement;
       
    48 import javax.tools.JavaCompiler;
       
    49 import javax.tools.JavaFileObject;
       
    50 import javax.tools.StandardJavaFileManager;
       
    51 import javax.tools.ToolProvider;
       
    52 
       
    53 import com.sun.source.util.JavacTask;
       
    54 import com.sun.tools.classfile.Attribute;
       
    55 import com.sun.tools.classfile.ClassFile;
       
    56 import com.sun.tools.classfile.ConstantPool;
       
    57 import com.sun.tools.classfile.ConstantPoolException;
       
    58 import com.sun.tools.classfile.Code_attribute;
       
    59 import com.sun.tools.classfile.ConstantPool.InvalidIndex;
       
    60 import com.sun.tools.classfile.ConstantPool.UnexpectedEntry;
       
    61 import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
       
    62 import com.sun.tools.classfile.LocalVariableTable_attribute;
       
    63 import com.sun.tools.classfile.Method;
       
    64 
       
    65 import static javax.tools.StandardLocation.*;
       
    66 import static com.sun.tools.classfile.LocalVariableTable_attribute.Entry;
       
    67 
       
    68 public class LVTHarness {
       
    69 
       
    70     static int nerrors = 0;
       
    71 
       
    72     static final JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
       
    73     static final StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
       
    74 
       
    75     public static void main(String[] args) throws Exception {
       
    76         fm.setLocation(SOURCE_PATH,
       
    77                 Arrays.asList(new File(System.getProperty("test.src"), "tests")));
       
    78         for (JavaFileObject jfo : fm.list(SOURCE_PATH, "",
       
    79                 Collections.singleton(JavaFileObject.Kind.SOURCE), true)) {
       
    80             new LVTHarness(jfo).check();
       
    81         }
       
    82         if (nerrors > 0) {
       
    83             throw new AssertionError("Errors were found");
       
    84         }
       
    85     }
       
    86 
       
    87 
       
    88     JavaFileObject jfo;
       
    89     Map<ElementKey, AliveRanges> aliveRangeMap =
       
    90             new HashMap<ElementKey, AliveRanges>();
       
    91     Set<String> declaredKeys = new HashSet<>();
       
    92     List<ElementKey> seenAliveRanges = new ArrayList<>();
       
    93 
       
    94     protected LVTHarness(JavaFileObject jfo) {
       
    95         this.jfo = jfo;
       
    96     }
       
    97 
       
    98     protected void check() throws Exception {
       
    99         JavacTask ct = (JavacTask)comp.getTask(null, fm, null, Arrays.asList("-g"),
       
   100                 null, Arrays.asList(jfo));
       
   101         System.err.println("compiling code " + jfo.toString());
       
   102         ct.setProcessors(Collections.singleton(new AliveRangeFinder()));
       
   103         if (!ct.call()) {
       
   104             throw new AssertionError("Error during compilation");
       
   105         }
       
   106 
       
   107         checkClassFile(new File(jfo.getName().replace(".java", ".class")));
       
   108 
       
   109         //check all candidates have been used up
       
   110         for (Map.Entry<ElementKey, AliveRanges> entry : aliveRangeMap.entrySet()) {
       
   111             if (!seenAliveRanges.contains(entry.getKey())) {
       
   112                 error("Redundant @AliveRanges annotation on method " +
       
   113                         entry.getKey().elem);
       
   114             }
       
   115         }
       
   116     }
       
   117 
       
   118     void checkClassFile(File file)
       
   119             throws IOException, ConstantPoolException, InvalidDescriptor {
       
   120         ClassFile classFile = ClassFile.read(file);
       
   121         ConstantPool constantPool = classFile.constant_pool;
       
   122 
       
   123         //lets get all the methods in the class file.
       
   124         for (Method method : classFile.methods) {
       
   125             for (ElementKey elementKey: aliveRangeMap.keySet()) {
       
   126                 String methodDesc = method.getName(constantPool) +
       
   127                         method.descriptor.getParameterTypes(constantPool);
       
   128                 if (methodDesc.equals(elementKey.elem.toString())) {
       
   129                     checkMethod(constantPool, method, aliveRangeMap.get(elementKey));
       
   130                     seenAliveRanges.add(elementKey);
       
   131                 }
       
   132             }
       
   133         }
       
   134     }
       
   135 
       
   136     void checkMethod(ConstantPool constantPool, Method method, AliveRanges ranges)
       
   137             throws InvalidIndex, UnexpectedEntry {
       
   138         Code_attribute code = (Code_attribute) method.attributes.get(Attribute.Code);
       
   139         LocalVariableTable_attribute lvt =
       
   140             (LocalVariableTable_attribute) (code.attributes.get(Attribute.LocalVariableTable));
       
   141         List<String> infoFromRanges = convertToStringList(ranges);
       
   142         List<String> infoFromLVT = convertToStringList(constantPool, lvt);
       
   143 
       
   144         // infoFromRanges most be contained in infoFromLVT
       
   145         int i = 0;
       
   146         int j = 0;
       
   147         while (i < infoFromRanges.size() && j < infoFromLVT.size()) {
       
   148             int comparison = infoFromRanges.get(i).compareTo(infoFromLVT.get(j));
       
   149             if (comparison == 0) {
       
   150                 i++; j++;
       
   151             } else if (comparison > 0) {
       
   152                 j++;
       
   153             } else {
       
   154                 break;
       
   155             }
       
   156         }
       
   157 
       
   158         if (i < infoFromRanges.size()) {
       
   159             error(infoFromLVT, infoFromRanges);
       
   160         }
       
   161     }
       
   162 
       
   163     List<String> convertToStringList(AliveRanges ranges) {
       
   164         List<String> result = new ArrayList<>();
       
   165         for (Annotation anno : ranges.value()) {
       
   166             AliveRange range = (AliveRange)anno;
       
   167             String str = formatLocalVariableData(range.varName(),
       
   168                     range.bytecodeStart(), range.bytecodeLength());
       
   169             result.add(str);
       
   170         }
       
   171         Collections.sort(result);
       
   172         return result;
       
   173     }
       
   174 
       
   175     List<String> convertToStringList(ConstantPool constantPool,
       
   176             LocalVariableTable_attribute lvt) throws InvalidIndex, UnexpectedEntry {
       
   177         List<String> result = new ArrayList<>();
       
   178         for (Entry entry : lvt.local_variable_table) {
       
   179             String str = formatLocalVariableData(constantPool.getUTF8Value(entry.name_index),
       
   180                     entry.start_pc, entry.length);
       
   181             result.add(str);
       
   182         }
       
   183         Collections.sort(result);
       
   184         return result;
       
   185     }
       
   186 
       
   187     String formatLocalVariableData(String varName, int start, int length) {
       
   188         StringBuilder sb = new StringBuilder()
       
   189                     .append("var name: ").append(varName)
       
   190                     .append(" start: ").append(start)
       
   191                     .append(" length: ").append(length);
       
   192         return sb.toString();
       
   193     }
       
   194 
       
   195     protected void error(List<String> infoFromLVT, List<String> infoFromRanges) {
       
   196         nerrors++;
       
   197         System.err.printf("Error occurred while checking file: %s\n", jfo.getName());
       
   198         System.err.println("The range info from the annotations is");
       
   199         printStringListToErrOutput(infoFromRanges);
       
   200         System.err.println();
       
   201         System.err.println("And the range info from the class file is");
       
   202         printStringListToErrOutput(infoFromLVT);
       
   203         System.err.println();
       
   204     }
       
   205 
       
   206     void printStringListToErrOutput(List<String> list) {
       
   207         for (String s : list) {
       
   208             System.err.println("\t" + s);
       
   209         }
       
   210     }
       
   211 
       
   212     protected void error(String msg) {
       
   213         nerrors++;
       
   214         System.err.printf("Error occurred while checking file: %s\nreason: %s\n",
       
   215                 jfo.getName(), msg);
       
   216     }
       
   217 
       
   218     class AliveRangeFinder extends JavacTestingAbstractProcessor {
       
   219 
       
   220         @Override
       
   221         public boolean process(Set<? extends TypeElement> annotations,
       
   222             RoundEnvironment roundEnv) {
       
   223             if (roundEnv.processingOver())
       
   224                 return true;
       
   225 
       
   226             TypeElement aliveRangeAnno = elements.getTypeElement("AliveRanges");
       
   227 
       
   228             if (!annotations.contains(aliveRangeAnno)) {
       
   229                 error("no @AliveRanges annotation found in test class");
       
   230             }
       
   231 
       
   232             for (Element elem: roundEnv.getElementsAnnotatedWith(aliveRangeAnno)) {
       
   233                 Annotation annotation = elem.getAnnotation(AliveRanges.class);
       
   234                 aliveRangeMap.put(new ElementKey(elem), (AliveRanges)annotation);
       
   235             }
       
   236             return true;
       
   237         }
       
   238     }
       
   239 
       
   240     class ElementKey {
       
   241 
       
   242         String key;
       
   243         Element elem;
       
   244 
       
   245         public ElementKey(Element elem) {
       
   246             this.elem = elem;
       
   247             this.key = computeKey(elem);
       
   248         }
       
   249 
       
   250         @Override
       
   251         public boolean equals(Object obj) {
       
   252             if (obj instanceof ElementKey) {
       
   253                 ElementKey other = (ElementKey)obj;
       
   254                 return other.key.equals(key);
       
   255             }
       
   256             return false;
       
   257         }
       
   258 
       
   259         @Override
       
   260         public int hashCode() {
       
   261             return key.hashCode();
       
   262         }
       
   263 
       
   264         String computeKey(Element e) {
       
   265             StringBuilder buf = new StringBuilder();
       
   266             while (e != null) {
       
   267                 buf.append(e.toString());
       
   268                 e = e.getEnclosingElement();
       
   269             }
       
   270             buf.append(jfo.getName());
       
   271             return buf.toString();
       
   272         }
       
   273 
       
   274         @Override
       
   275         public String toString() {
       
   276             return "Key{" + key + "}";
       
   277         }
       
   278     }
       
   279 
       
   280 }