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 
  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 
  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)
  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)
  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)
  95     public boolean isSubtype(TypeMirror t1, TypeMirror t2) {
  96         validateTypeNotIn(t1, EXEC_OR_PKG);
  97         validateTypeNotIn(t2, EXEC_OR_PKG);
  98         return types.isSubtype((Type) t1, (Type) t2);
  99     }
 100 
 101     @DefinedBy(Api.LANGUAGE_MODEL)
 102     public boolean isAssignable(TypeMirror t1, TypeMirror t2) {
 103         validateTypeNotIn(t1, EXEC_OR_PKG);
 104         validateTypeNotIn(t2, EXEC_OR_PKG);
 105         return types.isAssignable((Type) t1, (Type) t2);
 106     }
 107 
 108     @DefinedBy(Api.LANGUAGE_MODEL)
 109     public boolean contains(TypeMirror t1, TypeMirror t2) {
 110         validateTypeNotIn(t1, EXEC_OR_PKG);
 111         validateTypeNotIn(t2, EXEC_OR_PKG);
 112         return types.containsType((Type) t1, (Type) t2);
 113     }
 114 
 115     @DefinedBy(Api.LANGUAGE_MODEL)
 116     public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {
 117         return types.isSubSignature((Type) m1, (Type) m2);
 118     }
 119 
 120     @DefinedBy(Api.LANGUAGE_MODEL)
 121     public List<Type> directSupertypes(TypeMirror t) {
 122         validateTypeNotIn(t, EXEC_OR_PKG);
 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)
 130     public TypeMirror erasure(TypeMirror t) {
 131         if (t.getKind() == TypeKind.PACKAGE)
 132             throw new IllegalArgumentException(t.toString());
 133         return types.erasure((Type)t).stripMetadataIfNeeded();
 134     }
 135 
 136     @DefinedBy(Api.LANGUAGE_MODEL)
 137     public TypeElement boxedClass(PrimitiveType p) {
 138         return types.boxedClass((Type) p);
 139     }
 140 
 141     @DefinedBy(Api.LANGUAGE_MODEL)
 142     public PrimitiveType unboxedType(TypeMirror t) {
 143         if (t.getKind() != TypeKind.DECLARED)
 144             throw new IllegalArgumentException(t.toString());
 145         Type unboxed = types.unboxedType((Type) t);
 146         if (! unboxed.isPrimitive())    // only true primitives, not void
 147             throw new IllegalArgumentException(t.toString());
 148         return (PrimitiveType)unboxed;
 149     }
 150 
 151     @DefinedBy(Api.LANGUAGE_MODEL)
 152     public TypeMirror capture(TypeMirror t) {
 153         validateTypeNotIn(t, EXEC_OR_PKG);
 154         return types.capture((Type)t).stripMetadataIfNeeded();
 155     }
 156 
 157     @DefinedBy(Api.LANGUAGE_MODEL)
 158     public PrimitiveType getPrimitiveType(TypeKind kind) {
 159         switch (kind) {
 160         case BOOLEAN:   return syms.booleanType;
 161         case BYTE:      return syms.byteType;
 162         case SHORT:     return syms.shortType;
 163         case INT:       return syms.intType;
 164         case LONG:      return syms.longType;
 165         case CHAR:      return syms.charType;
 166         case FLOAT:     return syms.floatType;
 167         case DOUBLE:    return syms.doubleType;
 168         default:
 169             throw new IllegalArgumentException("Not a primitive type: " + kind);
 170         }
 171     }
 172 
 173     @DefinedBy(Api.LANGUAGE_MODEL)
 174     public NullType getNullType() {
 175         return (NullType) syms.botType;
 176     }
 177 
 178     @DefinedBy(Api.LANGUAGE_MODEL)
 179     public NoType getNoType(TypeKind kind) {
 180         switch (kind) {
 181         case VOID:      return syms.voidType;
 182         case NONE:      return Type.noType;
 183         default:
 184             throw new IllegalArgumentException(kind.toString());
 185         }
 186     }
 187 
 188     @DefinedBy(Api.LANGUAGE_MODEL)
 189     public ArrayType getArrayType(TypeMirror componentType) {
 190         switch (componentType.getKind()) {
 191         case VOID:
 192         case EXECUTABLE:
 193         case WILDCARD:  // heh!
 194         case PACKAGE:
 195             throw new IllegalArgumentException(componentType.toString());
 196         }
 197         return new Type.ArrayType((Type) componentType, syms.arrayClass);
 198     }
 199 
 200     @DefinedBy(Api.LANGUAGE_MODEL)
 201     public WildcardType getWildcardType(TypeMirror extendsBound,
 202                                         TypeMirror superBound) {
 203         BoundKind bkind;
 204         Type bound;
 205         if (extendsBound == null && superBound == null) {
 206             bkind = BoundKind.UNBOUND;
 207             bound = syms.objectType;
 208         } else if (superBound == null) {
 209             bkind = BoundKind.EXTENDS;
 210             bound = (Type) extendsBound;
 211         } else if (extendsBound == null) {
 212             bkind = BoundKind.SUPER;
 213             bound = (Type) superBound;
 214         } else {
 215             throw new IllegalArgumentException(
 216                     "Extends and super bounds cannot both be provided");
 217         }
 218         switch (bound.getKind()) {
 219         case ARRAY:
 220         case DECLARED:
 221         case ERROR:
 222         case TYPEVAR:
 223             return new Type.WildcardType(bound, bkind, syms.boundClass);
 224         default:
 225             throw new IllegalArgumentException(bound.toString());
 226         }
 227     }
 228 
 229     @DefinedBy(Api.LANGUAGE_MODEL)
 230     public DeclaredType getDeclaredType(TypeElement typeElem,
 231                                         TypeMirror... typeArgs) {
 232         ClassSymbol sym = (ClassSymbol) typeElem;
 233 
 234         if (typeArgs.length == 0)
 235             return (DeclaredType) sym.erasure(types);
 236         if (sym.type.getEnclosingType().isParameterized())
 237             throw new IllegalArgumentException(sym.toString());
 238 
 239         return getDeclaredType0(sym.type.getEnclosingType(), sym, typeArgs);
 240     }
 241 
 242     @DefinedBy(Api.LANGUAGE_MODEL)
 243     public DeclaredType getDeclaredType(DeclaredType enclosing,
 244                                         TypeElement typeElem,
 245                                         TypeMirror... typeArgs) {
 246         if (enclosing == null)
 247             return getDeclaredType(typeElem, typeArgs);
 248 
 249         ClassSymbol sym = (ClassSymbol) typeElem;
 250         Type outer = (Type) enclosing;
 251 
 252         if (outer.tsym != sym.owner.enclClass())
 253             throw new IllegalArgumentException(enclosing.toString());
 254         if (!outer.isParameterized())
 255             return getDeclaredType(typeElem, typeArgs);
 256 
 257         return getDeclaredType0(outer, sym, typeArgs);
 258     }
 259     // where
 260         private DeclaredType getDeclaredType0(Type outer,
 261                                               ClassSymbol sym,
 262                                               TypeMirror... typeArgs) {
 263             if (typeArgs.length != sym.type.getTypeArguments().length())
 264                 throw new IllegalArgumentException(
 265                 "Incorrect number of type arguments");
 266 
 267             ListBuffer<Type> targs = new ListBuffer<>();
 268             for (TypeMirror t : typeArgs) {
 269                 if (!(t instanceof ReferenceType || t instanceof WildcardType))
 270                     throw new IllegalArgumentException(t.toString());
 271                 targs.append((Type) t);
 272             }
 273             // TODO: Would like a way to check that type args match formals.
 274 
 275             return (DeclaredType) new Type.ClassType(outer, targs.toList(), sym);
 276         }
 277 
 278     /**
 279      * Returns the type of an element when that element is viewed as
 280      * a member of, or otherwise directly contained by, a given type.
 281      * For example,
 282      * when viewed as a member of the parameterized type {@code Set<String>},
 283      * the {@code Set.add} method is an {@code ExecutableType}
 284      * whose parameter is of type {@code String}.
 285      *
 286      * @param containing  the containing type
 287      * @param element     the element
 288      * @return the type of the element as viewed from the containing type
 289      * @throws IllegalArgumentException if the element is not a valid one
 290      *          for the given type
 291      */
 292     @DefinedBy(Api.LANGUAGE_MODEL)
 293     public TypeMirror asMemberOf(DeclaredType containing, Element element) {
 294         Type site = (Type)containing;
 295         Symbol sym = (Symbol)element;
 296         if (types.asSuper(site, sym.getEnclosingElement()) == null)
 297             throw new IllegalArgumentException(sym + "@" + site);
 298         return types.memberType(site, sym);
 299     }
 300 
 301 
 302     private static final Set<TypeKind> EXEC_OR_PKG =
 303             EnumSet.of(TypeKind.EXECUTABLE, TypeKind.PACKAGE);
 304 
 305     /**
 306      * Throws an IllegalArgumentException if a type's kind is one of a set.
 307      */
 308     private void validateTypeNotIn(TypeMirror t, Set<TypeKind> invalidKinds) {
 309         if (invalidKinds.contains(t.getKind()))
 310             throw new IllegalArgumentException(t.toString());
 311     }
 312 
 313     /**
 314      * Returns an object cast to the specified type.
 315      * @throws NullPointerException if the object is {@code null}
 316      * @throws IllegalArgumentException if the object is of the wrong type
 317      */
 318     private static <T> T cast(Class<T> clazz, Object o) {
 319         if (! clazz.isInstance(o))
 320             throw new IllegalArgumentException(o.toString());
 321         return clazz.cast(o);
 322     }
 323 
 324     public Set<MethodSymbol> getOverriddenMethods(Element elem) {
 325         if (elem.getKind() != ElementKind.METHOD
 326                 || elem.getModifiers().contains(Modifier.STATIC)
 327                 || elem.getModifiers().contains(Modifier.PRIVATE))
 328             return Collections.emptySet();
 329 
 330         if (!(elem instanceof MethodSymbol))
 331             throw new IllegalArgumentException();
 332 
 333         MethodSymbol m = (MethodSymbol) elem;
 334         ClassSymbol origin = (ClassSymbol) m.owner;
 335 
 336         Set<MethodSymbol> results = new LinkedHashSet<>();
 337         for (Type t : types.closure(origin.type)) {
 338             if (t != origin.type) {
 339                 ClassSymbol c = (ClassSymbol) t.tsym;
 340                 for (Symbol sym : c.members().getSymbolsByName(m.name)) {
 341                     if (sym.kind == MTH && m.overrides(sym, origin, types, true)) {
 342                         results.add((MethodSymbol) sym);
 343                     }
 344                 }
 345             }
 346         }
 347 
 348         return results;
 349     }
 350 }