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_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)
 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)
 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)
 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_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)
 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)
 138     public TypeElement boxedClass(PrimitiveType p) {
 139         return types.boxedClass((Type) p);
 140     }
 141 
 142     @DefinedBy(Api.LANGUAGE_MODEL)
 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)
 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)
 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)
 175     public NullType getNullType() {
 176         return (NullType) syms.botType;
 177     }
 178 
 179     @DefinedBy(Api.LANGUAGE_MODEL)
 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)
 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)
 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)
 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)
 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
 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)
 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      */
 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      */
 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 
 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 }