8026749: Missing LV table in lambda bodies

Fri, 18 Oct 2013 15:03:34 -0700

author
jjg
date
Fri, 18 Oct 2013 15:03:34 -0700
changeset 2146
7de97abc4a5c
parent 2145
7af634b1fc5b
child 2147
130b8c0e570e

8026749: Missing LV table in lambda bodies
Reviewed-by: vromero, jlahoda

src/share/classes/com/sun/tools/javac/code/Flags.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/Flow.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/jvm/Gen.java file | annotate | diff | comparison | revisions
test/tools/javac/lambda/LocalVariableTable.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/classes/com/sun/tools/javac/code/Flags.java	Thu Oct 17 19:10:19 2013 -0700
     1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Flags.java	Fri Oct 18 15:03:34 2013 -0700
     1.3 @@ -270,6 +270,11 @@
     1.4       */
     1.5      public static final long POTENTIALLY_AMBIGUOUS = 1L<<48;
     1.6  
     1.7 +    /**
     1.8 +     * Flag that marks a synthetic method body for a lambda expression
     1.9 +     */
    1.10 +    public static final long LAMBDA_METHOD = 1L<<49;
    1.11 +
    1.12      /** Modifier masks.
    1.13       */
    1.14      public static final int
    1.15 @@ -378,7 +383,8 @@
    1.16          NOT_IN_PROFILE(Flags.NOT_IN_PROFILE),
    1.17          BAD_OVERRIDE(Flags.BAD_OVERRIDE),
    1.18          SIGNATURE_POLYMORPHIC(Flags.SIGNATURE_POLYMORPHIC),
    1.19 -        THROWS(Flags.THROWS);
    1.20 +        THROWS(Flags.THROWS),
    1.21 +        LAMBDA_METHOD(Flags.LAMBDA_METHOD);
    1.22  
    1.23          Flag(long flag) {
    1.24              this.value = flag;
     2.1 --- a/src/share/classes/com/sun/tools/javac/comp/Flow.java	Thu Oct 17 19:10:19 2013 -0700
     2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Flow.java	Fri Oct 18 15:03:34 2013 -0700
     2.3 @@ -1718,9 +1718,9 @@
     2.4              if (tree.body == null) {
     2.5                  return;
     2.6              }
     2.7 -            /*  MemberEnter can generate synthetic methods, ignore them
     2.8 +            /*  Ignore synthetic methods, except for translated lambda methods.
     2.9               */
    2.10 -            if ((tree.sym.flags() & SYNTHETIC) != 0) {
    2.11 +            if ((tree.sym.flags() & (SYNTHETIC | LAMBDA_METHOD)) == SYNTHETIC) {
    2.12                  return;
    2.13              }
    2.14  
    2.15 @@ -1795,7 +1795,7 @@
    2.16          protected void initParam(JCVariableDecl def) {
    2.17              inits.incl(def.sym.adr);
    2.18              uninits.excl(def.sym.adr);
    2.19 -        }
    2.20 +            }
    2.21  
    2.22          public void visitVarDef(JCVariableDecl tree) {
    2.23              boolean track = trackable(tree.sym);
     3.1 --- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Thu Oct 17 19:10:19 2013 -0700
     3.2 +++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Fri Oct 18 15:03:34 2013 -0700
     3.3 @@ -30,6 +30,7 @@
     3.4  import com.sun.tools.javac.tree.TreeMaker;
     3.5  import com.sun.tools.javac.tree.TreeTranslator;
     3.6  import com.sun.tools.javac.code.Attribute;
     3.7 +import com.sun.tools.javac.code.Flags;
     3.8  import com.sun.tools.javac.code.Kinds;
     3.9  import com.sun.tools.javac.code.Scope;
    3.10  import com.sun.tools.javac.code.Symbol;
    3.11 @@ -1315,7 +1316,9 @@
    3.12                  ListBuffer<JCVariableDecl> paramBuff = new ListBuffer<JCVariableDecl>();
    3.13                  int i = 0;
    3.14                  for (List<Type> l = ptypes; l.nonEmpty(); l = l.tail) {
    3.15 -                    paramBuff.append(make.Param(make.paramName(i++), l.head, owner));
    3.16 +                    JCVariableDecl param = make.Param(make.paramName(i++), l.head, owner);
    3.17 +                    param.sym.pos = tree.pos;
    3.18 +                    paramBuff.append(param);
    3.19                  }
    3.20                  List<JCVariableDecl> params = paramBuff.toList();
    3.21  
    3.22 @@ -1755,7 +1758,7 @@
    3.23                          ((VarSymbol)ret).pos = ((VarSymbol)sym).pos;
    3.24                          break;
    3.25                      case CAPTURED_VAR:
    3.26 -                        ret = new VarSymbol(SYNTHETIC | FINAL, name, types.erasure(sym.type), translatedSym) {
    3.27 +                        ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym) {
    3.28                              @Override
    3.29                              public Symbol baseSymbol() {
    3.30                                  //keep mapping with original captured symbol
    3.31 @@ -1763,8 +1766,17 @@
    3.32                              }
    3.33                          };
    3.34                          break;
    3.35 +                    case LOCAL_VAR:
    3.36 +                        ret = new VarSymbol(FINAL, name, types.erasure(sym.type), translatedSym);
    3.37 +                        ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
    3.38 +                        break;
    3.39 +                    case PARAM:
    3.40 +                        ret = new VarSymbol(FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym);
    3.41 +                        ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
    3.42 +                        break;
    3.43                      default:
    3.44                          ret = makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
    3.45 +                        ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
    3.46                  }
    3.47                  if (ret != sym) {
    3.48                      ret.setDeclarationAttributes(sym.getRawAttributes());
    3.49 @@ -1845,7 +1857,7 @@
    3.50                  // If instance access isn't needed, make it static.
    3.51                  // Interface instance methods must be default methods.
    3.52                  // Lambda methods are private synthetic.
    3.53 -                translatedSym.flags_field = SYNTHETIC |
    3.54 +                translatedSym.flags_field = SYNTHETIC | LAMBDA_METHOD |
    3.55                          PRIVATE |
    3.56                          (thisReferenced? (inInterface? DEFAULT : 0) : STATIC);
    3.57  
     4.1 --- a/src/share/classes/com/sun/tools/javac/jvm/Gen.java	Thu Oct 17 19:10:19 2013 -0700
     4.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/Gen.java	Fri Oct 18 15:03:34 2013 -0700
     4.3 @@ -2892,7 +2892,8 @@
     4.4  
     4.5          @Override
     4.6          public void visitMethodDef(JCMethodDecl tree) {
     4.7 -            if ((tree.sym.flags() & (SYNTHETIC | GENERATEDCONSTR)) != 0) {
     4.8 +            if ((tree.sym.flags() & (SYNTHETIC | GENERATEDCONSTR)) != 0
     4.9 +                    && (tree.sym.flags() & LAMBDA_METHOD) == 0) {
    4.10                  return;
    4.11              }
    4.12              if (tree.name.equals(names.clinit)) {
    4.13 @@ -2906,6 +2907,7 @@
    4.14                  return;
    4.15              }
    4.16              currentMethod = tree.sym;
    4.17 +
    4.18              super.visitMethodDef(tree);
    4.19          }
    4.20  
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/test/tools/javac/lambda/LocalVariableTable.java	Fri Oct 18 15:03:34 2013 -0700
     5.3 @@ -0,0 +1,220 @@
     5.4 +/*
     5.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
     5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     5.7 + *
     5.8 + * This code is free software; you can redistribute it and/or modify it
     5.9 + * under the terms of the GNU General Public License version 2 only, as
    5.10 + * published by the Free Software Foundation.
    5.11 + *
    5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    5.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    5.15 + * version 2 for more details (a copy is included in the LICENSE file that
    5.16 + * accompanied this code).
    5.17 + *
    5.18 + * You should have received a copy of the GNU General Public License version
    5.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    5.21 + *
    5.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    5.23 + * or visit www.oracle.com if you need additional information or have any
    5.24 + * questions.
    5.25 + */
    5.26 +
    5.27 +/*
    5.28 + * @test
    5.29 + * @bug 8025998 8026749
    5.30 + * @summary Missing LV table in lambda bodies
    5.31 + * @compile -g LocalVariableTable.java
    5.32 + * @run main LocalVariableTable
    5.33 + */
    5.34 +
    5.35 +import java.io.*;
    5.36 +import java.lang.annotation.*;
    5.37 +import java.util.*;
    5.38 +import com.sun.tools.classfile.*;
    5.39 +
    5.40 +/*
    5.41 + * The test checks that a LocalVariableTable attribute is generated for the
    5.42 + * method bodies representing lambda expressions, and checks that the expected
    5.43 + * set of entries is found in the attribute.
    5.44 + *
    5.45 + * Since the bug was about missing entries in the LVT, not malformed entries,
    5.46 + * the test is not intended to be a detailed test of the contents of each
    5.47 + * LocalVariableTable entry: it is assumed that if a entry is present, it
    5.48 + * will have the correct contents.
    5.49 + *
    5.50 + * The test looks for test cases represented by nested classes whose
    5.51 + * name begins with "Lambda".  Each such class contains a lambda expression
    5.52 + * that will mapped into a lambda method, and because the test is compiled
    5.53 + * with -g, these methods should have a LocalVariableTable.  The set of
    5.54 + * expected names in the LVT is provided in an annotation on the class for
    5.55 + * the test case.
    5.56 + */
    5.57 +public class LocalVariableTable {
    5.58 +    public static void main(String... args) throws Exception {
    5.59 +        new LocalVariableTable().run();
    5.60 +    }
    5.61 +
    5.62 +    void run() throws Exception {
    5.63 +        // the declared classes are returned in an unspecified order,
    5.64 +        // so for neatness, sort them by name before processing them
    5.65 +        Class<?>[] classes = getClass().getDeclaredClasses();
    5.66 +        Arrays.sort(classes, (c1, c2) -> c1.getName().compareTo(c2.getName()));
    5.67 +
    5.68 +        for (Class<?> c : classes) {
    5.69 +            if (c.getSimpleName().startsWith("Lambda"))
    5.70 +                check(c);
    5.71 +        }
    5.72 +        if (errors > 0)
    5.73 +            throw new Exception(errors + " errors found");
    5.74 +    }
    5.75 +
    5.76 +    /** Check an individual test case. */
    5.77 +    void check(Class<?> c) throws Exception {
    5.78 +        System.err.println("Checking " + c.getSimpleName());
    5.79 +
    5.80 +        Expect expect = c.getAnnotation(Expect.class);
    5.81 +        if (expect == null) {
    5.82 +            error("@Expect not found for class " + c.getSimpleName());
    5.83 +            return;
    5.84 +        }
    5.85 +
    5.86 +        ClassFile cf = ClassFile.read(getClass().getResource(c.getName() + ".class").openStream());
    5.87 +        Method m = getLambdaMethod(cf);
    5.88 +        if (m == null) {
    5.89 +            error("lambda method not found");
    5.90 +            return;
    5.91 +        }
    5.92 +
    5.93 +        Code_attribute code = (Code_attribute) m.attributes.get(Attribute.Code);
    5.94 +        if (code == null) {
    5.95 +            error("Code attribute not found");
    5.96 +            return;
    5.97 +        }
    5.98 +
    5.99 +        LocalVariableTable_attribute lvt =
   5.100 +                (LocalVariableTable_attribute) code.attributes.get(Attribute.LocalVariableTable);
   5.101 +        if (lvt == null) {
   5.102 +            error("LocalVariableTable attribute not found");
   5.103 +            return;
   5.104 +        }
   5.105 +
   5.106 +        Set<String> foundNames = new LinkedHashSet<>();
   5.107 +        for (LocalVariableTable_attribute.Entry e: lvt.local_variable_table) {
   5.108 +            foundNames.add(cf.constant_pool.getUTF8Value(e.name_index));
   5.109 +        }
   5.110 +
   5.111 +        Set<String> expectNames = new LinkedHashSet<>(Arrays.asList(expect.value()));
   5.112 +        if (!foundNames.equals(expectNames)) {
   5.113 +            Set<String> foundOnly = new LinkedHashSet<>(foundNames);
   5.114 +            foundOnly.removeAll(expectNames);
   5.115 +            for (String s: foundOnly)
   5.116 +                error("Unexpected name found: " + s);
   5.117 +            Set<String> expectOnly = new LinkedHashSet<>(expectNames);
   5.118 +            expectOnly.removeAll(foundNames);
   5.119 +            for (String s: expectOnly)
   5.120 +                error("Expected name not found: " + s);
   5.121 +        }
   5.122 +    }
   5.123 +
   5.124 +    /** Get a method whose name begins "lambda$...". */
   5.125 +    Method getLambdaMethod(ClassFile cf) throws ConstantPoolException {
   5.126 +        for (Method m: cf.methods) {
   5.127 +            if (m.getName(cf.constant_pool).startsWith("lambda$"))
   5.128 +                return m;
   5.129 +        }
   5.130 +        return null;
   5.131 +    }
   5.132 +
   5.133 +    /** Report an error. */
   5.134 +    void error(String msg) {
   5.135 +        System.err.println("Error: " + msg);
   5.136 +        errors++;
   5.137 +    }
   5.138 +
   5.139 +    int errors;
   5.140 +
   5.141 +    /**
   5.142 +     * Annotation used to provide the set of names expected in the LVT attribute.
   5.143 +     */
   5.144 +    @Retention(RetentionPolicy.RUNTIME)
   5.145 +    @interface Expect {
   5.146 +        String[] value();
   5.147 +    }
   5.148 +
   5.149 +    /** Functional interface with nullary method. */
   5.150 +    interface Run0 {
   5.151 +        public void run();
   5.152 +    }
   5.153 +
   5.154 +    /** Functional interface with 1-ary method. */
   5.155 +    interface Run1 {
   5.156 +        public void run(int a0);
   5.157 +    }
   5.158 +
   5.159 +    /** Functional interface with 2-ary method. */
   5.160 +    interface Run2 {
   5.161 +        public void run(int a0, int a1);
   5.162 +    }
   5.163 +
   5.164 +    /*
   5.165 +     * ---------- Test cases ---------------------------------------------------
   5.166 +     */
   5.167 +
   5.168 +    @Expect({ "x" })
   5.169 +    static class Lambda_Args0_Local1 {
   5.170 +        Run0 r = () -> { int x = 0; };
   5.171 +    }
   5.172 +
   5.173 +    @Expect({ "x", "this" })
   5.174 +    static class Lambda_Args0_Local1_this {
   5.175 +        int v;
   5.176 +        Run0 r = () -> { int x = v; };
   5.177 +    }
   5.178 +
   5.179 +    @Expect({ "a" })
   5.180 +    static class Lambda_Args1_Local0 {
   5.181 +        Run1 r = (a) -> { };
   5.182 +    }
   5.183 +
   5.184 +    @Expect({ "a", "x" })
   5.185 +    static class Lambda_Args1_Local1 {
   5.186 +        Run1 r = (a) -> { int x = a; };
   5.187 +    }
   5.188 +
   5.189 +    @Expect({ "a", "x" })
   5.190 +    static class Lambda_Args1_Local1_Captured1 {
   5.191 +        void m() {
   5.192 +            int v = 0;
   5.193 +            Run1 r = (a) -> { int x = a + v; };
   5.194 +        }
   5.195 +    }
   5.196 +
   5.197 +    @Expect({ "a1", "a2", "x1", "x2", "this" })
   5.198 +    static class Lambda_Args2_Local2_Captured2_this {
   5.199 +        int v;
   5.200 +        void m() {
   5.201 +            int v1 = 0;
   5.202 +            int v2 = 0;
   5.203 +            Run2 r = (a1, a2) -> {
   5.204 +                int x1 = a1 + v1 + v;
   5.205 +                int x2 = a2 + v2 + v;
   5.206 +            };
   5.207 +        }
   5.208 +    }
   5.209 +
   5.210 +    @Expect({ "e" })
   5.211 +    static class Lambda_Try_Catch {
   5.212 +        private static Runnable asUncheckedRunnable(Closeable c) {
   5.213 +            return () -> {
   5.214 +                try {
   5.215 +                    c.close();
   5.216 +                } catch (IOException e) {
   5.217 +                   throw new UncheckedIOException(e);
   5.218 +                }
   5.219 +            };
   5.220 +        }
   5.221 +    }
   5.222 +}
   5.223 +

mercurial