Thu, 09 Oct 2008 16:21:04 +0100
6586091: javac crashes with StackOverflowError
Summary: Types.adapt should avoid infinite loops by exploiting a local cache
Reviewed-by: jjg
src/share/classes/com/sun/tools/javac/code/Types.java | file | annotate | diff | comparison | revisions | |
test/tools/javac/cast/6586091/T6586091.java | file | annotate | diff | comparison | revisions |
1.1 --- a/src/share/classes/com/sun/tools/javac/code/Types.java Thu Oct 09 16:19:13 2008 +0100 1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Types.java Thu Oct 09 16:21:04 2008 +0100 1.3 @@ -3213,6 +3213,7 @@ 1.4 containsType(t, s) && containsType(s, t); 1.5 } 1.6 1.7 + // <editor-fold defaultstate="collapsed" desc="adapt"> 1.8 /** 1.9 * Adapt a type by computing a substitution which maps a source 1.10 * type to a target type. 1.11 @@ -3226,94 +3227,115 @@ 1.12 Type target, 1.13 ListBuffer<Type> from, 1.14 ListBuffer<Type> to) throws AdaptFailure { 1.15 - Map<Symbol,Type> mapping = new HashMap<Symbol,Type>(); 1.16 - adaptRecursive(source, target, from, to, mapping); 1.17 - List<Type> fromList = from.toList(); 1.18 - List<Type> toList = to.toList(); 1.19 - while (!fromList.isEmpty()) { 1.20 - Type val = mapping.get(fromList.head.tsym); 1.21 - if (toList.head != val) 1.22 - toList.head = val; 1.23 - fromList = fromList.tail; 1.24 - toList = toList.tail; 1.25 + new Adapter(from, to).adapt(source, target); 1.26 + } 1.27 + 1.28 + class Adapter extends SimpleVisitor<Void, Type> { 1.29 + 1.30 + ListBuffer<Type> from; 1.31 + ListBuffer<Type> to; 1.32 + Map<Symbol,Type> mapping; 1.33 + 1.34 + Adapter(ListBuffer<Type> from, ListBuffer<Type> to) { 1.35 + this.from = from; 1.36 + this.to = to; 1.37 + mapping = new HashMap<Symbol,Type>(); 1.38 } 1.39 - } 1.40 - // where 1.41 - private void adaptRecursive(Type source, 1.42 - Type target, 1.43 - ListBuffer<Type> from, 1.44 - ListBuffer<Type> to, 1.45 - Map<Symbol,Type> mapping) throws AdaptFailure { 1.46 - if (source.tag == TYPEVAR) { 1.47 - // Check to see if there is 1.48 - // already a mapping for $source$, in which case 1.49 - // the old mapping will be merged with the new 1.50 - Type val = mapping.get(source.tsym); 1.51 - if (val != null) { 1.52 - if (val.isSuperBound() && target.isSuperBound()) { 1.53 - val = isSubtype(lowerBound(val), lowerBound(target)) 1.54 - ? target : val; 1.55 - } else if (val.isExtendsBound() && target.isExtendsBound()) { 1.56 - val = isSubtype(upperBound(val), upperBound(target)) 1.57 - ? val : target; 1.58 - } else if (!isSameType(val, target)) { 1.59 - throw new AdaptFailure(); 1.60 - } 1.61 - } else { 1.62 - val = target; 1.63 - from.append(source); 1.64 - to.append(target); 1.65 + 1.66 + public void adapt(Type source, Type target) throws AdaptFailure { 1.67 + visit(source, target); 1.68 + List<Type> fromList = from.toList(); 1.69 + List<Type> toList = to.toList(); 1.70 + while (!fromList.isEmpty()) { 1.71 + Type val = mapping.get(fromList.head.tsym); 1.72 + if (toList.head != val) 1.73 + toList.head = val; 1.74 + fromList = fromList.tail; 1.75 + toList = toList.tail; 1.76 + } 1.77 + } 1.78 + 1.79 + @Override 1.80 + public Void visitClassType(ClassType source, Type target) throws AdaptFailure { 1.81 + if (target.tag == CLASS) 1.82 + adaptRecursive(source.allparams(), target.allparams()); 1.83 + return null; 1.84 + } 1.85 + 1.86 + @Override 1.87 + public Void visitArrayType(ArrayType source, Type target) throws AdaptFailure { 1.88 + if (target.tag == ARRAY) 1.89 + adaptRecursive(elemtype(source), elemtype(target)); 1.90 + return null; 1.91 + } 1.92 + 1.93 + @Override 1.94 + public Void visitWildcardType(WildcardType source, Type target) throws AdaptFailure { 1.95 + if (source.isExtendsBound()) 1.96 + adaptRecursive(upperBound(source), upperBound(target)); 1.97 + else if (source.isSuperBound()) 1.98 + adaptRecursive(lowerBound(source), lowerBound(target)); 1.99 + return null; 1.100 + } 1.101 + 1.102 + @Override 1.103 + public Void visitTypeVar(TypeVar source, Type target) throws AdaptFailure { 1.104 + // Check to see if there is 1.105 + // already a mapping for $source$, in which case 1.106 + // the old mapping will be merged with the new 1.107 + Type val = mapping.get(source.tsym); 1.108 + if (val != null) { 1.109 + if (val.isSuperBound() && target.isSuperBound()) { 1.110 + val = isSubtype(lowerBound(val), lowerBound(target)) 1.111 + ? target : val; 1.112 + } else if (val.isExtendsBound() && target.isExtendsBound()) { 1.113 + val = isSubtype(upperBound(val), upperBound(target)) 1.114 + ? val : target; 1.115 + } else if (!isSameType(val, target)) { 1.116 + throw new AdaptFailure(); 1.117 } 1.118 - mapping.put(source.tsym, val); 1.119 - } else if (source.tag == target.tag) { 1.120 - switch (source.tag) { 1.121 - case CLASS: 1.122 - adapt(source.allparams(), target.allparams(), 1.123 - from, to, mapping); 1.124 - break; 1.125 - case ARRAY: 1.126 - adaptRecursive(elemtype(source), elemtype(target), 1.127 - from, to, mapping); 1.128 - break; 1.129 - case WILDCARD: 1.130 - if (source.isExtendsBound()) { 1.131 - adaptRecursive(upperBound(source), upperBound(target), 1.132 - from, to, mapping); 1.133 - } else if (source.isSuperBound()) { 1.134 - adaptRecursive(lowerBound(source), lowerBound(target), 1.135 - from, to, mapping); 1.136 - } 1.137 - break; 1.138 + } else { 1.139 + val = target; 1.140 + from.append(source); 1.141 + to.append(target); 1.142 + } 1.143 + mapping.put(source.tsym, val); 1.144 + return null; 1.145 + } 1.146 + 1.147 + @Override 1.148 + public Void visitType(Type source, Type target) { 1.149 + return null; 1.150 + } 1.151 + 1.152 + private Set<TypePair> cache = new HashSet<TypePair>(); 1.153 + 1.154 + private void adaptRecursive(Type source, Type target) { 1.155 + TypePair pair = new TypePair(source, target); 1.156 + if (cache.add(pair)) { 1.157 + try { 1.158 + visit(source, target); 1.159 + } finally { 1.160 + cache.remove(pair); 1.161 } 1.162 } 1.163 } 1.164 - public static class AdaptFailure extends Exception { 1.165 - static final long serialVersionUID = -7490231548272701566L; 1.166 - } 1.167 - 1.168 - /** 1.169 - * Adapt a type by computing a substitution which maps a list of 1.170 - * source types to a list of target types. 1.171 - * 1.172 - * @param source the source type 1.173 - * @param target the target type 1.174 - * @param from the type variables of the computed substitution 1.175 - * @param to the types of the computed substitution. 1.176 - */ 1.177 - private void adapt(List<Type> source, 1.178 - List<Type> target, 1.179 - ListBuffer<Type> from, 1.180 - ListBuffer<Type> to, 1.181 - Map<Symbol,Type> mapping) throws AdaptFailure { 1.182 - if (source.length() == target.length()) { 1.183 - while (source.nonEmpty()) { 1.184 - adaptRecursive(source.head, target.head, from, to, mapping); 1.185 - source = source.tail; 1.186 - target = target.tail; 1.187 + 1.188 + private void adaptRecursive(List<Type> source, List<Type> target) { 1.189 + if (source.length() == target.length()) { 1.190 + while (source.nonEmpty()) { 1.191 + adaptRecursive(source.head, target.head); 1.192 + source = source.tail; 1.193 + target = target.tail; 1.194 + } 1.195 } 1.196 } 1.197 } 1.198 1.199 + public static class AdaptFailure extends RuntimeException { 1.200 + static final long serialVersionUID = -7490231548272701566L; 1.201 + } 1.202 + 1.203 private void adaptSelf(Type t, 1.204 ListBuffer<Type> from, 1.205 ListBuffer<Type> to) { 1.206 @@ -3326,6 +3348,7 @@ 1.207 throw new AssertionError(ex); 1.208 } 1.209 } 1.210 + // </editor-fold> 1.211 1.212 /** 1.213 * Rewrite all type variables (universal quantifiers) in the given
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/tools/javac/cast/6586091/T6586091.java Thu Oct 09 16:21:04 2008 +0100 2.3 @@ -0,0 +1,38 @@ 2.4 +/* 2.5 + * Copyright 2008 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 2.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 2.24 + * have any questions. 2.25 + */ 2.26 + 2.27 +/* 2.28 + * @test 2.29 + * @author Maurizio Cimadamore 2.30 + * @bug 6586091 2.31 + * @summary javac crashes with StackOverflowError 2.32 + * @compile T6586091.java 2.33 + */ 2.34 + 2.35 +class T6586091 { 2.36 + static class A<T extends A<?>> {} 2.37 + static class B extends A<A<?>> {} 2.38 + 2.39 + A<A<?>> t = null; 2.40 + B c = (B)t; 2.41 +} 2.42 \ No newline at end of file