test/tools/javac/lambda/LocalVariableTable.java

Mon, 14 Oct 2013 23:07:43 -0700

author
jjg
date
Mon, 14 Oct 2013 23:07:43 -0700
changeset 2114
09a414673570
permissions
-rw-r--r--

8025998: Missing LV table in lambda bodies
Reviewed-by: vromero

jjg@2114 1 /*
jjg@2114 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
jjg@2114 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jjg@2114 4 *
jjg@2114 5 * This code is free software; you can redistribute it and/or modify it
jjg@2114 6 * under the terms of the GNU General Public License version 2 only, as
jjg@2114 7 * published by the Free Software Foundation.
jjg@2114 8 *
jjg@2114 9 * This code is distributed in the hope that it will be useful, but WITHOUT
jjg@2114 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jjg@2114 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jjg@2114 12 * version 2 for more details (a copy is included in the LICENSE file that
jjg@2114 13 * accompanied this code).
jjg@2114 14 *
jjg@2114 15 * You should have received a copy of the GNU General Public License version
jjg@2114 16 * 2 along with this work; if not, write to the Free Software Foundation,
jjg@2114 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jjg@2114 18 *
jjg@2114 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jjg@2114 20 * or visit www.oracle.com if you need additional information or have any
jjg@2114 21 * questions.
jjg@2114 22 */
jjg@2114 23
jjg@2114 24 /*
jjg@2114 25 * @test
jjg@2114 26 * @bug 8025998
jjg@2114 27 * @summary Missing LV table in lambda bodies
jjg@2114 28 * @compile -g LocalVariableTable.java
jjg@2114 29 * @run main LocalVariableTable
jjg@2114 30 */
jjg@2114 31
jjg@2114 32 import java.lang.annotation.*;
jjg@2114 33 import java.util.*;
jjg@2114 34 import com.sun.tools.classfile.*;
jjg@2114 35
jjg@2114 36 /*
jjg@2114 37 * The test checks that a LocalVariableTable attribute is generated for the
jjg@2114 38 * method bodies representing lambda expressions, and checks that the expected
jjg@2114 39 * set of entries is found in the attribute.
jjg@2114 40 *
jjg@2114 41 * Since the bug was about missing entries in the LVT, not malformed entries,
jjg@2114 42 * the test is not intended to be a detailed test of the contents of each
jjg@2114 43 * LocalVariableTable entry: it is assumed that if a entry is present, it
jjg@2114 44 * will have the correct contents.
jjg@2114 45 *
jjg@2114 46 * The test looks for test cases represented by nested classes whose
jjg@2114 47 * name begins with "Lambda". Each such class contains a lambda expression
jjg@2114 48 * that will mapped into a lambda method, and because the test is compiled
jjg@2114 49 * with -g, these methods should have a LocalVariableTable. The set of
jjg@2114 50 * expected names in the LVT is provided in an annotation on the class for
jjg@2114 51 * the test case.
jjg@2114 52 */
jjg@2114 53 public class LocalVariableTable {
jjg@2114 54 public static void main(String... args) throws Exception {
jjg@2114 55 new LocalVariableTable().run();
jjg@2114 56 }
jjg@2114 57
jjg@2114 58 void run() throws Exception {
jjg@2114 59 // the declared classes are returned in an unspecified order,
jjg@2114 60 // so for neatness, sort them by name before processing them
jjg@2114 61 Class<?>[] classes = getClass().getDeclaredClasses();
jjg@2114 62 Arrays.sort(classes, (c1, c2) -> c1.getName().compareTo(c2.getName()));
jjg@2114 63
jjg@2114 64 for (Class<?> c : classes) {
jjg@2114 65 if (c.getSimpleName().startsWith("Lambda"))
jjg@2114 66 check(c);
jjg@2114 67 }
jjg@2114 68 if (errors > 0)
jjg@2114 69 throw new Exception(errors + " errors found");
jjg@2114 70 }
jjg@2114 71
jjg@2114 72 /** Check an individual test case. */
jjg@2114 73 void check(Class<?> c) throws Exception {
jjg@2114 74 System.err.println("Checking " + c.getSimpleName());
jjg@2114 75
jjg@2114 76 Expect expect = c.getAnnotation(Expect.class);
jjg@2114 77 if (expect == null) {
jjg@2114 78 error("@Expect not found for class " + c.getSimpleName());
jjg@2114 79 return;
jjg@2114 80 }
jjg@2114 81
jjg@2114 82 ClassFile cf = ClassFile.read(getClass().getResource(c.getName() + ".class").openStream());
jjg@2114 83 Method m = getLambdaMethod(cf);
jjg@2114 84 if (m == null) {
jjg@2114 85 error("lambda method not found");
jjg@2114 86 return;
jjg@2114 87 }
jjg@2114 88
jjg@2114 89 Code_attribute code = (Code_attribute) m.attributes.get(Attribute.Code);
jjg@2114 90 if (code == null) {
jjg@2114 91 error("Code attribute not found");
jjg@2114 92 return;
jjg@2114 93 }
jjg@2114 94
jjg@2114 95 LocalVariableTable_attribute lvt =
jjg@2114 96 (LocalVariableTable_attribute) code.attributes.get(Attribute.LocalVariableTable);
jjg@2114 97 if (lvt == null) {
jjg@2114 98 error("LocalVariableTable attribute not found");
jjg@2114 99 return;
jjg@2114 100 }
jjg@2114 101
jjg@2114 102 Set<String> foundNames = new LinkedHashSet<>();
jjg@2114 103 for (LocalVariableTable_attribute.Entry e: lvt.local_variable_table) {
jjg@2114 104 foundNames.add(cf.constant_pool.getUTF8Value(e.name_index));
jjg@2114 105 }
jjg@2114 106
jjg@2114 107 Set<String> expectNames = new LinkedHashSet<>(Arrays.asList(expect.value()));
jjg@2114 108 if (!foundNames.equals(expectNames)) {
jjg@2114 109 Set<String> foundOnly = new LinkedHashSet<>(foundNames);
jjg@2114 110 foundOnly.removeAll(expectNames);
jjg@2114 111 for (String s: foundOnly)
jjg@2114 112 error("Unexpected name found: " + s);
jjg@2114 113 Set<String> expectOnly = new LinkedHashSet<>(expectNames);
jjg@2114 114 expectOnly.removeAll(foundNames);
jjg@2114 115 for (String s: expectOnly)
jjg@2114 116 error("Expected name not found: " + s);
jjg@2114 117 }
jjg@2114 118 }
jjg@2114 119
jjg@2114 120 /** Get a method whose name begins "lambda$...". */
jjg@2114 121 Method getLambdaMethod(ClassFile cf) throws ConstantPoolException {
jjg@2114 122 for (Method m: cf.methods) {
jjg@2114 123 if (m.getName(cf.constant_pool).startsWith("lambda$"))
jjg@2114 124 return m;
jjg@2114 125 }
jjg@2114 126 return null;
jjg@2114 127 }
jjg@2114 128
jjg@2114 129 /** Report an error. */
jjg@2114 130 void error(String msg) {
jjg@2114 131 System.err.println("Error: " + msg);
jjg@2114 132 errors++;
jjg@2114 133 }
jjg@2114 134
jjg@2114 135 int errors;
jjg@2114 136
jjg@2114 137 /**
jjg@2114 138 * Annotation used to provide the set of names expected in the LVT attribute.
jjg@2114 139 */
jjg@2114 140 @Retention(RetentionPolicy.RUNTIME)
jjg@2114 141 @interface Expect {
jjg@2114 142 String[] value();
jjg@2114 143 }
jjg@2114 144
jjg@2114 145 /** Functional interface with nullary method. */
jjg@2114 146 interface Run0 {
jjg@2114 147 public void run();
jjg@2114 148 }
jjg@2114 149
jjg@2114 150 /** Functional interface with 1-ary method. */
jjg@2114 151 interface Run1 {
jjg@2114 152 public void run(int a0);
jjg@2114 153 }
jjg@2114 154
jjg@2114 155 /** Functional interface with 2-ary method. */
jjg@2114 156 interface Run2 {
jjg@2114 157 public void run(int a0, int a1);
jjg@2114 158 }
jjg@2114 159
jjg@2114 160 /*
jjg@2114 161 * ---------- Test cases ---------------------------------------------------
jjg@2114 162 */
jjg@2114 163
jjg@2114 164 @Expect({ "x" })
jjg@2114 165 static class Lambda_Args0_Local1 {
jjg@2114 166 Run0 r = () -> { int x = 0; };
jjg@2114 167 }
jjg@2114 168
jjg@2114 169 @Expect({ "x", "this" })
jjg@2114 170 static class Lambda_Args0_Local1_this {
jjg@2114 171 int v;
jjg@2114 172 Run0 r = () -> { int x = v; };
jjg@2114 173 }
jjg@2114 174
jjg@2114 175 @Expect({ "a" })
jjg@2114 176 static class Lambda_Args1_Local0 {
jjg@2114 177 Run1 r = (a) -> { };
jjg@2114 178 }
jjg@2114 179
jjg@2114 180 @Expect({ "a", "x" })
jjg@2114 181 static class Lambda_Args1_Local1 {
jjg@2114 182 Run1 r = (a) -> { int x = a; };
jjg@2114 183 }
jjg@2114 184
jjg@2114 185 @Expect({ "a", "x" })
jjg@2114 186 static class Lambda_Args1_Local1_Captured1 {
jjg@2114 187 void m() {
jjg@2114 188 int v = 0;
jjg@2114 189 Run1 r = (a) -> { int x = a + v; };
jjg@2114 190 }
jjg@2114 191 }
jjg@2114 192
jjg@2114 193 @Expect({ "a1", "a2", "x1", "x2", "this" })
jjg@2114 194 static class Lambda_Args2_Local2_Captured2_this {
jjg@2114 195 int v;
jjg@2114 196 void m() {
jjg@2114 197 int v1 = 0;
jjg@2114 198 int v2 = 0;
jjg@2114 199 Run2 r = (a1, a2) -> {
jjg@2114 200 int x1 = a1 + v1 + v;
jjg@2114 201 int x2 = a2 + v2 + v;
jjg@2114 202 };
jjg@2114 203 }
jjg@2114 204 }
jjg@2114 205 }
jjg@2114 206

mercurial