1/* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.jshell; 27 28import java.util.HashSet; 29import com.sun.tools.javac.code.Type; 30import com.sun.tools.javac.code.Type.ClassType; 31import com.sun.tools.javac.util.JavacMessages; 32import java.util.Locale; 33import java.util.Set; 34import java.util.function.BinaryOperator; 35import com.sun.tools.javac.code.BoundKind; 36import com.sun.tools.javac.code.Flags; 37import com.sun.tools.javac.code.Symtab; 38import com.sun.tools.javac.code.Type.CapturedType; 39import com.sun.tools.javac.code.Type.StructuralTypeMapping; 40import com.sun.tools.javac.code.Type.TypeVar; 41import com.sun.tools.javac.code.Type.WildcardType; 42import com.sun.tools.javac.code.Types; 43import com.sun.tools.javac.code.Types.SimpleVisitor; 44import com.sun.tools.javac.util.List; 45import static com.sun.tools.javac.code.BoundKind.EXTENDS; 46import static com.sun.tools.javac.code.BoundKind.SUPER; 47import static com.sun.tools.javac.code.BoundKind.UNBOUND; 48import static com.sun.tools.javac.code.Type.ArrayType; 49import static com.sun.tools.javac.code.TypeTag.BOT; 50import static com.sun.tools.javac.code.TypeTag.WILDCARD; 51 52/** 53 * Print variable types in source form. 54 * TypeProjection and CaptureScanner are copied from Types in the JEP-286 55 * Sandbox by Maurizio. The checks for Non-Denotable in TypePrinter are 56 * cribbed from denotableChecker of the same source. 57 * 58 * @author Maurizio Cimadamore 59 * @author Robert Field 60 */ 61class VarTypePrinter extends TypePrinter { 62 private static final String WILD = "?"; 63 64 private final Symtab syms; 65 private final Types types; 66 67 VarTypePrinter(JavacMessages messages, BinaryOperator<String> fullClassNameAndPackageToClass, 68 Symtab syms, Types types) { 69 super(messages, fullClassNameAndPackageToClass); 70 this.syms = syms; 71 this.types = types; 72 } 73 74 @Override 75 String toString(Type t) { 76 return super.toString(upward(t)); 77 } 78 79 @Override 80 public String visitTypeVar(TypeVar t, Locale locale) { 81 /* Any type variable mentioned in the inferred type must have been declared as a type parameter 82 (i.e cannot have been produced by inference (18.4)) 83 */ 84 // and beyond that, there are no global type vars, so if there are any 85 // type variables left, they need to be eliminated 86 return WILD; // Non-denotable 87 } 88 89 @Override 90 public String visitCapturedType(CapturedType t, Locale locale) { 91 /* Any type variable mentioned in the inferred type must have been declared as a type parameter 92 (i.e cannot have been produced by capture conversion (5.1.10)) 93 */ 94 return WILD; // Non-denotable 95 } 96 97 public Type upward(Type t) { 98 List<Type> captures = captures(t); 99 return upward(t, captures); 100 } 101 102 /************* Following from JEP-286 Types.java ***********/ 103 104 public Type upward(Type t, List<Type> vars) { 105 return t.map(new TypeProjection(vars), true); 106 } 107 108 public List<Type> captures(Type t) { 109 CaptureScanner cs = new CaptureScanner(); 110 Set<Type> captures = new HashSet<>(); 111 cs.visit(t, captures); 112 return List.from(captures); 113 } 114 115 class CaptureScanner extends SimpleVisitor<Void, Set<Type>> { 116 117 @Override 118 public Void visitType(Type t, Set<Type> types) { 119 return null; 120 } 121 122 @Override 123 public Void visitClassType(ClassType t, Set<Type> seen) { 124 if (t.isCompound()) { 125 types.directSupertypes(t).forEach(s -> visit(s, seen)); 126 } else { 127 t.allparams().forEach(ta -> visit(ta, seen)); 128 } 129 return null; 130 } 131 132 @Override 133 public Void visitArrayType(ArrayType t, Set<Type> seen) { 134 return visit(t.elemtype, seen); 135 } 136 137 @Override 138 public Void visitWildcardType(WildcardType t, Set<Type> seen) { 139 visit(t.type, seen); 140 return null; 141 } 142 143 @Override 144 public Void visitTypeVar(TypeVar t, Set<Type> seen) { 145 if ((t.tsym.flags() & Flags.SYNTHETIC) != 0 && seen.add(t)) { 146 visit(t.getUpperBound(), seen); 147 } 148 return null; 149 } 150 151 @Override 152 public Void visitCapturedType(CapturedType t, Set<Type> seen) { 153 if (seen.add(t)) { 154 visit(t.getUpperBound(), seen); 155 visit(t.getLowerBound(), seen); 156 } 157 return null; 158 } 159 } 160 161 class TypeProjection extends StructuralTypeMapping<Boolean> { 162 163 List<Type> vars; 164 Set<Type> seen = new HashSet<>(); 165 166 public TypeProjection(List<Type> vars) { 167 this.vars = vars; 168 } 169 170 @Override 171 public Type visitClassType(ClassType t, Boolean upward) { 172 if (upward && !t.isCompound() && t.tsym.name.isEmpty()) { 173 //lift anonymous class type to first supertype (class or interface) 174 return types.directSupertypes(t).last(); 175 } else if (t.isCompound()) { 176 List<Type> components = types.directSupertypes(t); 177 List<Type> components1 = components.map(c -> c.map(this, upward)); 178 if (components == components1) return t; 179 else return types.makeIntersectionType(components1); 180 } else { 181 Type outer = t.getEnclosingType(); 182 Type outer1 = visit(outer, upward); 183 List<Type> typarams = t.getTypeArguments(); 184 List<Type> typarams1 = typarams.map(ta -> mapTypeArgument(ta, upward)); 185 if (typarams1.stream().anyMatch(ta -> ta.hasTag(BOT))) { 186 //not defined 187 return syms.botType; 188 } 189 if (outer1 == outer && typarams1 == typarams) return t; 190 else return new ClassType(outer1, typarams1, t.tsym, t.getMetadata()) { 191 @Override 192 protected boolean needsStripping() { 193 return true; 194 } 195 }; 196 } 197 } 198 199 protected Type makeWildcard(Type upper, Type lower) { 200 BoundKind bk; 201 Type bound; 202 if (upper.hasTag(BOT)) { 203 upper = syms.objectType; 204 } 205 boolean isUpperObject = types.isSameType(upper, syms.objectType); 206 if (!lower.hasTag(BOT) && isUpperObject) { 207 bound = lower; 208 bk = SUPER; 209 } else { 210 bound = upper; 211 bk = isUpperObject ? UNBOUND : EXTENDS; 212 } 213 return new WildcardType(bound, bk, syms.boundClass); 214 } 215 216 @Override 217 public Type visitTypeVar(TypeVar t, Boolean upward) { 218 if (vars.contains(t)) { 219 try { 220 if (seen.add(t)) { 221 return (upward ? 222 t.getUpperBound() : 223 (t.getLowerBound() == null) ? 224 syms.botType : 225 t.getLowerBound()) 226 .map(this, upward); 227 } else { 228 //cycle 229 return syms.objectType; 230 } 231 } finally { 232 seen.remove(t); 233 } 234 } else { 235 return t; 236 } 237 } 238 239 @Override 240 public Type visitWildcardType(WildcardType wt, Boolean upward) { 241 if (upward) { 242 return wt.isExtendsBound() ? 243 wt.type.map(this, upward) : 244 syms.objectType; 245 } else { 246 return wt.isSuperBound() ? 247 wt.type.map(this, upward) : 248 syms.botType; 249 } 250 } 251 252 private Type mapTypeArgument(Type t, boolean upward) { 253 if (!t.containsAny(vars)) { 254 return t; 255 } else if (!t.hasTag(WILDCARD) && !upward) { 256 //not defined 257 return syms.botType; 258 } else { 259 Type upper = t.map(this, upward); 260 Type lower = t.map(this, !upward); 261 return makeWildcard(upper, lower); 262 } 263 } 264 } 265} 266