test/tools/javac/lambda/LocalVariableTable.java

Thu, 26 Mar 2015 11:34:50 +0100

author
jlahoda
date
Thu, 26 Mar 2015 11:34:50 +0100
changeset 2734
ba758e1ffa69
parent 0
959103a6100f
permissions
-rw-r--r--

8054220: Debugger doesn't show variables *outside* lambda
8058227: Debugger has no access to outer variables inside Lambda
Summary: Put local variables captured by lambda into the lambda method's LocalVariableTable.
Reviewed-by: mcimadamore, rfield

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

mercurial