Fri, 30 May 2014 12:54:16 +0200
8031967: For some sources compiler compiles for ever
Summary: Avoid creating DeferredTypes for method calls with method calls as receivers if the site can be determined reliably
Reviewed-by: mcimadamore, vromero
Contributed-by: maurizio.cimadamore@oracle.com, jan.lahoda@oracle.com
src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java | file | annotate | diff | comparison | revisions | |
test/tools/javac/lambda/T8031967.java | file | annotate | diff | comparison | revisions |
1.1 --- a/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Fri May 30 18:21:05 2014 +0100 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Fri May 30 12:54:16 2014 +0200 1.3 @@ -1218,25 +1218,102 @@ 1.4 } 1.5 1.6 //slow path 1.7 + Symbol sym = quicklyResolveMethod(env, tree); 1.8 + 1.9 + if (sym == null) { 1.10 + result = ArgumentExpressionKind.POLY; 1.11 + return; 1.12 + } 1.13 + 1.14 + result = analyzeCandidateMethods(sym, ArgumentExpressionKind.PRIMITIVE, 1.15 + argumentKindAnalyzer); 1.16 + } 1.17 + //where 1.18 + private boolean isSimpleReceiver(JCTree rec) { 1.19 + switch (rec.getTag()) { 1.20 + case IDENT: 1.21 + return true; 1.22 + case SELECT: 1.23 + return isSimpleReceiver(((JCFieldAccess)rec).selected); 1.24 + case TYPEAPPLY: 1.25 + case TYPEARRAY: 1.26 + return true; 1.27 + case ANNOTATED_TYPE: 1.28 + return isSimpleReceiver(((JCAnnotatedType)rec).underlyingType); 1.29 + case APPLY: 1.30 + return true; 1.31 + default: 1.32 + return false; 1.33 + } 1.34 + } 1.35 + private ArgumentExpressionKind reduce(ArgumentExpressionKind kind) { 1.36 + return argumentKindAnalyzer.reduce(result, kind); 1.37 + } 1.38 + MethodAnalyzer<ArgumentExpressionKind> argumentKindAnalyzer = 1.39 + new MethodAnalyzer<ArgumentExpressionKind>() { 1.40 + @Override 1.41 + public ArgumentExpressionKind process(MethodSymbol ms) { 1.42 + return ArgumentExpressionKind.methodKind(ms, types); 1.43 + } 1.44 + @Override 1.45 + public ArgumentExpressionKind reduce(ArgumentExpressionKind kind1, 1.46 + ArgumentExpressionKind kind2) { 1.47 + switch (kind1) { 1.48 + case PRIMITIVE: return kind2; 1.49 + case NO_POLY: return kind2.isPoly() ? kind2 : kind1; 1.50 + case POLY: return kind1; 1.51 + default: 1.52 + Assert.error(); 1.53 + return null; 1.54 + } 1.55 + } 1.56 + @Override 1.57 + public boolean shouldStop(ArgumentExpressionKind result) { 1.58 + return result.isPoly(); 1.59 + } 1.60 + }; 1.61 + 1.62 + @Override 1.63 + public void visitLiteral(JCLiteral tree) { 1.64 + Type litType = attr.litType(tree.typetag); 1.65 + result = ArgumentExpressionKind.standaloneKind(litType, types); 1.66 + } 1.67 + 1.68 + @Override 1.69 + void skip(JCTree tree) { 1.70 + result = ArgumentExpressionKind.NO_POLY; 1.71 + } 1.72 + 1.73 + private Symbol quicklyResolveMethod(Env<AttrContext> env, final JCMethodInvocation tree) { 1.74 final JCExpression rec = tree.meth.hasTag(SELECT) ? 1.75 ((JCFieldAccess)tree.meth).selected : 1.76 null; 1.77 1.78 if (rec != null && !isSimpleReceiver(rec)) { 1.79 - //give up if receiver is too complex (to cut down analysis time) 1.80 - result = ArgumentExpressionKind.POLY; 1.81 - return; 1.82 + return null; 1.83 } 1.84 1.85 - Type site = rec != null ? 1.86 - attribSpeculative(rec, env, attr.unknownTypeExprInfo).type : 1.87 - env.enclClass.sym.type; 1.88 + Type site; 1.89 1.90 - while (site.hasTag(TYPEVAR)) { 1.91 - site = site.getUpperBound(); 1.92 + if (rec != null) { 1.93 + if (rec.hasTag(APPLY)) { 1.94 + Symbol recSym = quicklyResolveMethod(env, (JCMethodInvocation) rec); 1.95 + if (recSym == null) 1.96 + return null; 1.97 + Symbol resolvedReturnType = 1.98 + analyzeCandidateMethods(recSym, syms.errSymbol, returnSymbolAnalyzer); 1.99 + if (resolvedReturnType == null) 1.100 + return null; 1.101 + site = resolvedReturnType.type; 1.102 + } else { 1.103 + site = attribSpeculative(rec, env, attr.unknownTypeExprInfo).type; 1.104 + } 1.105 + } else { 1.106 + site = env.enclClass.sym.type; 1.107 } 1.108 1.109 List<Type> args = rs.dummyArgs(tree.args.length()); 1.110 + Name name = TreeInfo.name(tree.meth); 1.111 1.112 Resolve.LookupHelper lh = rs.new LookupHelper(name, site, args, List.<Type>nil(), MethodResolutionPhase.VARARITY) { 1.113 @Override 1.114 @@ -1251,61 +1328,60 @@ 1.115 } 1.116 }; 1.117 1.118 - Symbol sym = rs.lookupMethod(env, tree, site.tsym, rs.arityMethodCheck, lh); 1.119 + return rs.lookupMethod(env, tree, site.tsym, rs.arityMethodCheck, lh); 1.120 + } 1.121 + //where: 1.122 + MethodAnalyzer<Symbol> returnSymbolAnalyzer = new MethodAnalyzer<Symbol>() { 1.123 + @Override 1.124 + public Symbol process(MethodSymbol ms) { 1.125 + ArgumentExpressionKind kind = ArgumentExpressionKind.methodKind(ms, types); 1.126 + return kind != ArgumentExpressionKind.POLY ? ms.getReturnType().tsym : null; 1.127 + } 1.128 + @Override 1.129 + public Symbol reduce(Symbol s1, Symbol s2) { 1.130 + return s1 == syms.errSymbol ? s2 : s1 == s2 ? s1 : null; 1.131 + } 1.132 + @Override 1.133 + public boolean shouldStop(Symbol result) { 1.134 + return result == null; 1.135 + } 1.136 + }; 1.137 1.138 - if (sym.kind == Kinds.AMBIGUOUS) { 1.139 - Resolve.AmbiguityError err = (Resolve.AmbiguityError)sym.baseSymbol(); 1.140 - result = ArgumentExpressionKind.PRIMITIVE; 1.141 - for (Symbol s : err.ambiguousSyms) { 1.142 - if (result.isPoly()) break; 1.143 - if (s.kind == Kinds.MTH) { 1.144 - result = reduce(ArgumentExpressionKind.methodKind(s, types)); 1.145 + /** 1.146 + * Process the result of Resolve.lookupMethod. If sym is a method symbol, the result of 1.147 + * MethodAnalyzer.process is returned. If sym is an ambiguous symbol, all the candidate 1.148 + * methods are inspected one by one, using MethodAnalyzer.process. The outcomes are 1.149 + * reduced using MethodAnalyzer.reduce (using defaultValue as the first value over which 1.150 + * the reduction runs). MethodAnalyzer.shouldStop can be used to stop the inspection early. 1.151 + */ 1.152 + <E> E analyzeCandidateMethods(Symbol sym, E defaultValue, MethodAnalyzer<E> analyzer) { 1.153 + switch (sym.kind) { 1.154 + case Kinds.MTH: 1.155 + return analyzer.process((MethodSymbol) sym); 1.156 + case Kinds.AMBIGUOUS: 1.157 + Resolve.AmbiguityError err = (Resolve.AmbiguityError)sym.baseSymbol(); 1.158 + E res = defaultValue; 1.159 + for (Symbol s : err.ambiguousSyms) { 1.160 + if (s.kind == Kinds.MTH) { 1.161 + res = analyzer.reduce(res, analyzer.process((MethodSymbol) s)); 1.162 + if (analyzer.shouldStop(res)) 1.163 + return res; 1.164 + } 1.165 } 1.166 - } 1.167 - } else { 1.168 - result = (sym.kind == Kinds.MTH) ? 1.169 - ArgumentExpressionKind.methodKind(sym, types) : 1.170 - ArgumentExpressionKind.NO_POLY; 1.171 + return res; 1.172 + default: 1.173 + return defaultValue; 1.174 } 1.175 } 1.176 - //where 1.177 - private boolean isSimpleReceiver(JCTree rec) { 1.178 - switch (rec.getTag()) { 1.179 - case IDENT: 1.180 - return true; 1.181 - case SELECT: 1.182 - return isSimpleReceiver(((JCFieldAccess)rec).selected); 1.183 - case TYPEAPPLY: 1.184 - case TYPEARRAY: 1.185 - return true; 1.186 - case ANNOTATED_TYPE: 1.187 - return isSimpleReceiver(((JCAnnotatedType)rec).underlyingType); 1.188 - default: 1.189 - return false; 1.190 - } 1.191 - } 1.192 - private ArgumentExpressionKind reduce(ArgumentExpressionKind kind) { 1.193 - switch (result) { 1.194 - case PRIMITIVE: return kind; 1.195 - case NO_POLY: return kind.isPoly() ? kind : result; 1.196 - case POLY: return result; 1.197 - default: 1.198 - Assert.error(); 1.199 - return null; 1.200 - } 1.201 - } 1.202 + } 1.203 1.204 - @Override 1.205 - public void visitLiteral(JCLiteral tree) { 1.206 - Type litType = attr.litType(tree.typetag); 1.207 - result = ArgumentExpressionKind.standaloneKind(litType, types); 1.208 - } 1.209 + /** Analyzer for methods - used by analyzeCandidateMethods. */ 1.210 + interface MethodAnalyzer<E> { 1.211 + E process(MethodSymbol ms); 1.212 + E reduce(E e1, E e2); 1.213 + boolean shouldStop(E result); 1.214 + } 1.215 1.216 - @Override 1.217 - void skip(JCTree tree) { 1.218 - result = ArgumentExpressionKind.NO_POLY; 1.219 - } 1.220 - } 1.221 //where 1.222 private EnumSet<JCTree.Tag> deferredCheckerTags = 1.223 EnumSet.of(LAMBDA, REFERENCE, PARENS, TYPECAST,
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/tools/javac/lambda/T8031967.java Fri May 30 12:54:16 2014 +0200 2.3 @@ -0,0 +1,137 @@ 2.4 +/* 2.5 + * Copyright (c) 2014, 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. 2.11 + * 2.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 2.15 + * version 2 for more details (a copy is included in the LICENSE file that 2.16 + * accompanied this code). 2.17 + * 2.18 + * You should have received a copy of the GNU General Public License version 2.19 + * 2 along with this work; if not, write to the Free Software Foundation, 2.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.21 + * 2.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2.23 + * or visit www.oracle.com if you need additional information or have any 2.24 + * questions. 2.25 + */ 2.26 + 2.27 +/* 2.28 + * @test 2.29 + * @bug 8031967 2.30 + * @summary Ensure javac can handle very deeply nested chain of method invocations occurring as 2.31 + * a parameter to other method invocations. 2.32 + * @run main T8031967 2.33 + */ 2.34 + 2.35 +import java.io.IOException; 2.36 +import java.net.URI; 2.37 +import java.util.Arrays; 2.38 +import java.util.List; 2.39 + 2.40 +import javax.tools.DiagnosticListener; 2.41 +import javax.tools.JavaCompiler; 2.42 +import javax.tools.JavaFileObject; 2.43 +import javax.tools.SimpleJavaFileObject; 2.44 +import javax.tools.ToolProvider; 2.45 + 2.46 +import com.sun.source.util.JavacTask; 2.47 + 2.48 +public class T8031967 { 2.49 + 2.50 + public static void main(String... args) throws IOException { 2.51 + new T8031967().run(); 2.52 + } 2.53 + 2.54 + final int depth = 50; 2.55 + 2.56 + private void run() throws IOException { 2.57 + runTestCase(true); 2.58 + runTestCase(false); 2.59 + } 2.60 + 2.61 + private void runTestCase(boolean withErrors) throws IOException { 2.62 + StringBuilder code = new StringBuilder(); 2.63 + 2.64 + code.append("public class Test {\n" + 2.65 + " private void test() {\n" + 2.66 + " GroupLayout l = new GroupLayout();\n" + 2.67 + " l.setHorizontalGroup(\n"); 2.68 + 2.69 + gen(code, depth); 2.70 + code.append(" );\n" + 2.71 + " }\n"); 2.72 + if (!withErrors) { 2.73 + code.append(" class GroupLayout {\n" + 2.74 + " ParallelGroup createParallelGroup() {return null;}\n" + 2.75 + " ParallelGroup createParallelGroup(int i) {return null;}\n" + 2.76 + " ParallelGroup createParallelGroup(int i, int j) {return null;}\n" + 2.77 + " void setHorizontalGroup(Group g) { }\n" + 2.78 + " }\n" + 2.79 + " \n" + 2.80 + " class Group {\n" + 2.81 + " Group addGroup(Group g) { return this; }\n" + 2.82 + " Group addGroup(int i, Group g) { return this; }\n" + 2.83 + " Group addGap(int i) { return this; }\n" + 2.84 + " Group addGap(long l) { return this; }\n" + 2.85 + " Group addGap(int i, int j) { return this; }\n" + 2.86 + " Group addComponent(Object c) { return this; }\n" + 2.87 + " Group addComponent(int i, Object c) { return this; }\n" + 2.88 + " }\n" + 2.89 + " class ParallelGroup extends Group {\n" + 2.90 + " Group addGroup(Group g) { return this; }\n" + 2.91 + " Group addGroup(int i, Group g) { return this; }\n" + 2.92 + " Group addGap(int i) { return this; }\n" + 2.93 + " Group addGap(int i, int j) { return this; }\n" + 2.94 + " Group addComponent(Object c) { return this; }\n" + 2.95 + " Group addComponent(int i, Object c) { return this; }\n" + 2.96 + " }\n"); 2.97 + } 2.98 + 2.99 + code.append("}\n"); 2.100 + 2.101 + JavaSource source = new JavaSource(code.toString()); 2.102 + List<JavaSource> sourceList = Arrays.asList(source); 2.103 + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 2.104 + DiagnosticListener<JavaFileObject> noErrors = (diagnostic) -> { 2.105 + throw new IllegalStateException("Should not produce errors: " + diagnostic); 2.106 + }; 2.107 + JavacTask task = (JavacTask) compiler.getTask(null, null, withErrors ? null : noErrors, 2.108 + null, null, sourceList); 2.109 + 2.110 + task.analyze(); 2.111 + } 2.112 + 2.113 + private void gen(StringBuilder code, int depth) { 2.114 + code.append("l.createParallelGroup()\n"); 2.115 + if (depth > 0) { 2.116 + code.append(".addGroup(\n"); 2.117 + gen(code, depth - 1); 2.118 + code.append(")"); 2.119 + } 2.120 + 2.121 + code.append(".addGap(1)\n" + 2.122 + ".addComponent(new Object())\n" + 2.123 + ".addGap(1)\n" + 2.124 + ".addComponent(new Object())"); 2.125 + } 2.126 + 2.127 + class JavaSource extends SimpleJavaFileObject { 2.128 + 2.129 + final String code; 2.130 + public JavaSource(String code) { 2.131 + super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 2.132 + this.code = code; 2.133 + } 2.134 + 2.135 + @Override 2.136 + public CharSequence getCharContent(boolean ignoreEncodingErrors) { 2.137 + return code; 2.138 + } 2.139 + } 2.140 +}