VarTypePrinter.java revision 4038:95c92c634f60
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