1 /*
2  * Copyright (c) 2005, 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 
26 package com.sun.tools.javac.model;
27 
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.EnumSet;
31 import java.util.LinkedHashSet;
32 import java.util.List;
33 import java.util.Set;
34 import java.util.stream.Collectors;
35 
36 import javax.lang.model.element.*;
37 import javax.lang.model.type.*;
38 
39 import com.sun.tools.javac.code.*;
40 import com.sun.tools.javac.code.Symbol.*;
41 import com.sun.tools.javac.util.*;
42 import com.sun.tools.javac.util.DefinedBy.Api;
43 
44 import static com.sun.tools.javac.code.Kinds.Kind.*;
45 
46 /**
47  * Utility methods for operating on types.
48  *
49  * <p><b>This is NOT part of any supported API.
50  * If you write code that depends on this, you do so at your own
51  * risk.  This code and its internal interfaces are subject to change
52  * or deletion without notice.</b></p>
53  */
54 public class JavacTypes implements javax.lang.model.util.Types {
55 
56     private final Symtab syms;
57     private final Types types;
58 
instance(Context context)59     public static JavacTypes instance(Context context) {
60         JavacTypes instance = context.get(JavacTypes.class);
61         if (instance == null)
62             instance = new JavacTypes(context);
63         return instance;
64     }
65 
JavacTypes(Context context)66     protected JavacTypes(Context context) {
67         context.put(JavacTypes.class, this);
68         syms = Symtab.instance(context);
69         types = Types.instance(context);
70     }
71 
72     @DefinedBy(Api.LANGUAGE_MODEL)
asElement(TypeMirror t)73     public Element asElement(TypeMirror t) {
74         switch (t.getKind()) {
75             case DECLARED:
76             case INTERSECTION:
77             case ERROR:
78             case TYPEVAR:
79                 Type type = cast(Type.class, t);
80                 return type.asElement();
81             default:
82                 return null;
83         }
84     }
85 
86     @DefinedBy(Api.LANGUAGE_MODEL)
isSameType(TypeMirror t1, TypeMirror t2)87     public boolean isSameType(TypeMirror t1, TypeMirror t2) {
88         if (t1.getKind() == TypeKind.WILDCARD || t2.getKind() == TypeKind.WILDCARD) {
89             return false;
90         }
91         return types.isSameType((Type) t1, (Type) t2);
92     }
93 
94     @DefinedBy(Api.LANGUAGE_MODEL)
isSubtype(TypeMirror t1, TypeMirror t2)95     public boolean isSubtype(TypeMirror t1, TypeMirror t2) {
96         validateTypeNotIn(t1, EXEC_OR_PKG_OR_MOD);
97         validateTypeNotIn(t2, EXEC_OR_PKG_OR_MOD);
98         return types.isSubtype((Type) t1, (Type) t2);
99     }
100 
101     @DefinedBy(Api.LANGUAGE_MODEL)
isAssignable(TypeMirror t1, TypeMirror t2)102     public boolean isAssignable(TypeMirror t1, TypeMirror t2) {
103         validateTypeNotIn(t1, EXEC_OR_PKG_OR_MOD);
104         validateTypeNotIn(t2, EXEC_OR_PKG_OR_MOD);
105         return types.isAssignable((Type) t1, (Type) t2);
106     }
107 
108     @DefinedBy(Api.LANGUAGE_MODEL)
contains(TypeMirror t1, TypeMirror t2)109     public boolean contains(TypeMirror t1, TypeMirror t2) {
110         validateTypeNotIn(t1, EXEC_OR_PKG_OR_MOD);
111         validateTypeNotIn(t2, EXEC_OR_PKG_OR_MOD);
112         return types.containsType((Type) t1, (Type) t2);
113     }
114 
115     @DefinedBy(Api.LANGUAGE_MODEL)
isSubsignature(ExecutableType m1, ExecutableType m2)116     public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {
117         return types.isSubSignature((Type) m1, (Type) m2);
118     }
119 
120     @DefinedBy(Api.LANGUAGE_MODEL)
directSupertypes(TypeMirror t)121     public List<Type> directSupertypes(TypeMirror t) {
122         validateTypeNotIn(t, EXEC_OR_PKG_OR_MOD);
123         Type ty = (Type)t;
124         return types.directSupertypes(ty).stream()
125                 .map(Type::stripMetadataIfNeeded)
126                 .collect(Collectors.toList());
127     }
128 
129     @DefinedBy(Api.LANGUAGE_MODEL)
erasure(TypeMirror t)130     public TypeMirror erasure(TypeMirror t) {
131         TypeKind kind = t.getKind();
132         if (kind == TypeKind.PACKAGE || kind == TypeKind.MODULE)
133             throw new IllegalArgumentException(t.toString());
134         return types.erasure((Type)t).stripMetadataIfNeeded();
135     }
136 
137     @DefinedBy(Api.LANGUAGE_MODEL)
boxedClass(PrimitiveType p)138     public TypeElement boxedClass(PrimitiveType p) {
139         return types.boxedClass((Type) p);
140     }
141 
142     @DefinedBy(Api.LANGUAGE_MODEL)
unboxedType(TypeMirror t)143     public PrimitiveType unboxedType(TypeMirror t) {
144         if (t.getKind() != TypeKind.DECLARED)
145             throw new IllegalArgumentException(t.toString());
146         Type unboxed = types.unboxedType((Type) t);
147         if (! unboxed.isPrimitive())    // only true primitives, not void
148             throw new IllegalArgumentException(t.toString());
149         return (PrimitiveType)unboxed;
150     }
151 
152     @DefinedBy(Api.LANGUAGE_MODEL)
capture(TypeMirror t)153     public TypeMirror capture(TypeMirror t) {
154         validateTypeNotIn(t, EXEC_OR_PKG_OR_MOD);
155         return types.capture((Type)t).stripMetadataIfNeeded();
156     }
157 
158     @DefinedBy(Api.LANGUAGE_MODEL)
getPrimitiveType(TypeKind kind)159     public PrimitiveType getPrimitiveType(TypeKind kind) {
160         switch (kind) {
161         case BOOLEAN:   return syms.booleanType;
162         case BYTE:      return syms.byteType;
163         case SHORT:     return syms.shortType;
164         case INT:       return syms.intType;
165         case LONG:      return syms.longType;
166         case CHAR:      return syms.charType;
167         case FLOAT:     return syms.floatType;
168         case DOUBLE:    return syms.doubleType;
169         default:
170             throw new IllegalArgumentException("Not a primitive type: " + kind);
171         }
172     }
173 
174     @DefinedBy(Api.LANGUAGE_MODEL)
getNullType()175     public NullType getNullType() {
176         return (NullType) syms.botType;
177     }
178 
179     @DefinedBy(Api.LANGUAGE_MODEL)
getNoType(TypeKind kind)180     public NoType getNoType(TypeKind kind) {
181         switch (kind) {
182         case VOID:      return syms.voidType;
183         case NONE:      return Type.noType;
184         default:
185             throw new IllegalArgumentException(kind.toString());
186         }
187     }
188 
189     @DefinedBy(Api.LANGUAGE_MODEL)
getArrayType(TypeMirror componentType)190     public ArrayType getArrayType(TypeMirror componentType) {
191         switch (componentType.getKind()) {
192         case VOID:
193         case EXECUTABLE:
194         case WILDCARD:  // heh!
195         case PACKAGE:
196         case MODULE:
197             throw new IllegalArgumentException(componentType.toString());
198         }
199         return new Type.ArrayType((Type) componentType, syms.arrayClass);
200     }
201 
202     @DefinedBy(Api.LANGUAGE_MODEL)
getWildcardType(TypeMirror extendsBound, TypeMirror superBound)203     public WildcardType getWildcardType(TypeMirror extendsBound,
204                                         TypeMirror superBound) {
205         BoundKind bkind;
206         Type bound;
207         if (extendsBound == null && superBound == null) {
208             bkind = BoundKind.UNBOUND;
209             bound = syms.objectType;
210         } else if (superBound == null) {
211             bkind = BoundKind.EXTENDS;
212             bound = (Type) extendsBound;
213         } else if (extendsBound == null) {
214             bkind = BoundKind.SUPER;
215             bound = (Type) superBound;
216         } else {
217             throw new IllegalArgumentException(
218                     "Extends and super bounds cannot both be provided");
219         }
220         switch (bound.getKind()) {
221         case ARRAY:
222         case DECLARED:
223         case ERROR:
224         case TYPEVAR:
225             return new Type.WildcardType(bound, bkind, syms.boundClass);
226         default:
227             throw new IllegalArgumentException(bound.toString());
228         }
229     }
230 
231     @DefinedBy(Api.LANGUAGE_MODEL)
getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs)232     public DeclaredType getDeclaredType(TypeElement typeElem,
233                                         TypeMirror... typeArgs) {
234         ClassSymbol sym = (ClassSymbol) typeElem;
235 
236         if (typeArgs.length == 0)
237             return (DeclaredType) sym.erasure(types);
238         if (sym.type.getEnclosingType().isParameterized())
239             throw new IllegalArgumentException(sym.toString());
240 
241         return getDeclaredType0(sym.type.getEnclosingType(), sym, typeArgs);
242     }
243 
244     @DefinedBy(Api.LANGUAGE_MODEL)
getDeclaredType(DeclaredType enclosing, TypeElement typeElem, TypeMirror... typeArgs)245     public DeclaredType getDeclaredType(DeclaredType enclosing,
246                                         TypeElement typeElem,
247                                         TypeMirror... typeArgs) {
248         if (enclosing == null)
249             return getDeclaredType(typeElem, typeArgs);
250 
251         ClassSymbol sym = (ClassSymbol) typeElem;
252         Type outer = (Type) enclosing;
253 
254         if (outer.tsym != sym.owner.enclClass())
255             throw new IllegalArgumentException(enclosing.toString());
256         if (!outer.isParameterized())
257             return getDeclaredType(typeElem, typeArgs);
258 
259         return getDeclaredType0(outer, sym, typeArgs);
260     }
261     // where
getDeclaredType0(Type outer, ClassSymbol sym, TypeMirror... typeArgs)262         private DeclaredType getDeclaredType0(Type outer,
263                                               ClassSymbol sym,
264                                               TypeMirror... typeArgs) {
265             if (typeArgs.length != sym.type.getTypeArguments().length())
266                 throw new IllegalArgumentException(
267                 "Incorrect number of type arguments");
268 
269             ListBuffer<Type> targs = new ListBuffer<>();
270             for (TypeMirror t : typeArgs) {
271                 if (!(t instanceof ReferenceType || t instanceof WildcardType))
272                     throw new IllegalArgumentException(t.toString());
273                 targs.append((Type) t);
274             }
275             // TODO: Would like a way to check that type args match formals.
276 
277             return (DeclaredType) new Type.ClassType(outer, targs.toList(), sym);
278         }
279 
280     /**
281      * Returns the type of an element when that element is viewed as
282      * a member of, or otherwise directly contained by, a given type.
283      * For example,
284      * when viewed as a member of the parameterized type {@code Set<String>},
285      * the {@code Set.add} method is an {@code ExecutableType}
286      * whose parameter is of type {@code String}.
287      *
288      * @param containing  the containing type
289      * @param element     the element
290      * @return the type of the element as viewed from the containing type
291      * @throws IllegalArgumentException if the element is not a valid one
292      *          for the given type
293      */
294     @DefinedBy(Api.LANGUAGE_MODEL)
asMemberOf(DeclaredType containing, Element element)295     public TypeMirror asMemberOf(DeclaredType containing, Element element) {
296         Type site = (Type)containing;
297         Symbol sym = (Symbol)element;
298         if (types.asSuper(site, sym.getEnclosingElement()) == null)
299             throw new IllegalArgumentException(sym + "@" + site);
300         return types.memberType(site, sym);
301     }
302 
303 
304     private static final Set<TypeKind> EXEC_OR_PKG_OR_MOD =
305         EnumSet.of(TypeKind.EXECUTABLE, TypeKind.PACKAGE, TypeKind.MODULE);
306 
307     /**
308      * Throws an IllegalArgumentException if a type's kind is one of a set.
309      */
validateTypeNotIn(TypeMirror t, Set<TypeKind> invalidKinds)310     private void validateTypeNotIn(TypeMirror t, Set<TypeKind> invalidKinds) {
311         if (invalidKinds.contains(t.getKind()))
312             throw new IllegalArgumentException(t.toString());
313     }
314 
315     /**
316      * Returns an object cast to the specified type.
317      * @throws NullPointerException if the object is {@code null}
318      * @throws IllegalArgumentException if the object is of the wrong type
319      */
cast(Class<T> clazz, Object o)320     private static <T> T cast(Class<T> clazz, Object o) {
321         if (! clazz.isInstance(o))
322             throw new IllegalArgumentException(o.toString());
323         return clazz.cast(o);
324     }
325 
getOverriddenMethods(Element elem)326     public Set<MethodSymbol> getOverriddenMethods(Element elem) {
327         if (elem.getKind() != ElementKind.METHOD
328                 || elem.getModifiers().contains(Modifier.STATIC)
329                 || elem.getModifiers().contains(Modifier.PRIVATE))
330             return Collections.emptySet();
331 
332         if (!(elem instanceof MethodSymbol))
333             throw new IllegalArgumentException();
334 
335         MethodSymbol m = (MethodSymbol) elem;
336         ClassSymbol origin = (ClassSymbol) m.owner;
337 
338         Set<MethodSymbol> results = new LinkedHashSet<>();
339         for (Type t : types.closure(origin.type)) {
340             if (t != origin.type) {
341                 ClassSymbol c = (ClassSymbol) t.tsym;
342                 for (Symbol sym : c.members().getSymbolsByName(m.name)) {
343                     if (sym.kind == MTH && m.overrides(sym, origin, types, true)) {
344                         results.add((MethodSymbol) sym);
345                     }
346                 }
347             }
348         }
349 
350         return results;
351     }
352 }
353