/* * Copyright 2016-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.jvm.java.abi.source; import com.facebook.buck.util.liteinfersupport.Nullable; import com.facebook.buck.util.liteinfersupport.Preconditions; import java.util.Arrays; import java.util.List; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.IntersectionType; import javax.lang.model.type.NoType; import javax.lang.model.type.NullType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.Types; /** * An implementation of {@link Types} using just the AST of a single module, without its * dependencies. Of necessity, such an implementation will need to make assumptions about the * meanings of some names, and thus must be used with care. See documentation for individual methods * and {@link com.facebook.buck.jvm.java.abi.source} for more information. */ class TreeBackedTypes implements Types { private final Types javacTypes; @Nullable private TreeBackedElements elements; TreeBackedTypes(Types javacTypes) { this.javacTypes = javacTypes; } /* package */ void setElements(TreeBackedElements elements) { this.elements = elements; } private TreeBackedElements getElements() { return Preconditions.checkNotNull(elements); } @Override @Nullable public Element asElement(TypeMirror t) { if (t.getKind() == TypeKind.DECLARED) { return ((DeclaredType) t).asElement(); } else if (t.getKind() == TypeKind.TYPEVAR) { throw new UnsupportedOperationException("Type variables not yet implemented"); } return null; } @Override public boolean isSameType(TypeMirror t1, TypeMirror t2) { boolean t1Artificial = isArtificialType(t1); boolean t2Artificial = isArtificialType(t2); if (!t1Artificial && !t2Artificial) { return javacTypes.isSameType(t1, t2); } // IMPORTANT: We can't early-out on reference equality, because wildcard types are never // considered the same type as one another. if (t1Artificial != t2Artificial || t1.getKind() != t2.getKind()) { return false; } assert t1Artificial && t2Artificial; switch (t1.getKind()) { case BOOLEAN: case BYTE: case CHAR: case SHORT: case INT: case LONG: case FLOAT: case DOUBLE: case NULL: return true; case WILDCARD: return false; // Wildcard types are never the same as one another; see docs case DECLARED: return isSameDeclaredType((DeclaredType) t1, (DeclaredType) t2); case TYPEVAR: return isSameTypeVariable((TypeVariable) t1, (TypeVariable) t2); case INTERSECTION: return isSameIntersectionType((IntersectionType) t1, (IntersectionType) t2); case ARRAY: return isSameType(((ArrayType) t1).getComponentType(), ((ArrayType) t2).getComponentType()); //$CASES-OMITTED$ default: throw new UnsupportedOperationException( String.format("isSameType NYI for kind %s", t1.getKind())); } } private boolean isSameDeclaredType(DeclaredType t1, DeclaredType t2) { if (!t1.asElement().equals(t2.asElement())) { return false; } List<? extends TypeMirror> args1 = t1.getTypeArguments(); List<? extends TypeMirror> args2 = t2.getTypeArguments(); if (args1.size() != args2.size()) { // TODO(jkeljo): This is impossible in code that will compile, but we will eventually need // to gracefully error out on code with some kinds of errors (such as providing the // wrong number of type arguments). Whenever we do that work, the body of this statement // should throw. return false; } for (int i = 0; i < args1.size(); i++) { if (!isSameType(args1.get(i), args2.get(i))) { return false; } } return true; } private boolean isSameTypeVariable(TypeVariable t1, TypeVariable t2) { if (!t1.asElement().equals(t2.asElement())) { return false; } // TODO(jkeljo): Upper and lower bounds could also matter if the type variable is the result of // capture conversion, but we don't support capture conversion yet (or maybe ever). return true; } private boolean isSameIntersectionType(IntersectionType t1, IntersectionType t2) { List<? extends TypeMirror> t1Bounds = t1.getBounds(); List<? extends TypeMirror> t2Bounds = t2.getBounds(); if (t1Bounds.size() != t2Bounds.size()) { return false; } for (TypeMirror t1Bound : t1Bounds) { if (!listContainsType(t2Bounds, t1Bound)) { return false; } } return true; } private boolean listContainsType(List<? extends TypeMirror> list, TypeMirror type) { boolean found = false; for (TypeMirror t2Bound : list) { if (isSameType(type, t2Bound)) { found = true; break; } } return found; } @Override public boolean isSubtype(TypeMirror t1, TypeMirror t2) { throw new UnsupportedOperationException(); } @Override public boolean isAssignable(TypeMirror t1, TypeMirror t2) { throw new UnsupportedOperationException(); } @Override public boolean contains(TypeMirror t1, TypeMirror t2) { throw new UnsupportedOperationException(); } @Override public boolean isSubsignature(ExecutableType m1, ExecutableType m2) { throw new UnsupportedOperationException(); } @Override public List<? extends TypeMirror> directSupertypes(TypeMirror t) { throw new UnsupportedOperationException(); } @Override public TypeMirror erasure(TypeMirror t) { if (isArtificialType(t)) { return erasure((StandaloneTypeMirror) t); } return javacTypes.erasure(t); } private TypeMirror erasure(StandaloneTypeMirror t) { switch (t.getKind()) { case ARRAY: { ArrayType arrayType = (ArrayType) t; return getArrayType(erasure(arrayType.getComponentType())); } case DECLARED: { DeclaredType declaredType = (DeclaredType) t; TypeElement typeElement = (TypeElement) declaredType.asElement(); if (declaredType.getEnclosingType().getKind() == TypeKind.DECLARED) { DeclaredType enclosingType = (DeclaredType) declaredType.getEnclosingType(); return getDeclaredType((DeclaredType) erasure(enclosingType), typeElement); } return getDeclaredType(typeElement); } // $CASES-OMITTED$ default: throw new UnsupportedOperationException(); } } @Override public TypeElement boxedClass(PrimitiveType p) { throw new UnsupportedOperationException(); } @Override public PrimitiveType unboxedType(TypeMirror t) { throw new UnsupportedOperationException(); } @Override public TypeMirror capture(TypeMirror t) { throw new UnsupportedOperationException(); } @Override public PrimitiveType getPrimitiveType(TypeKind kind) { return javacTypes.getPrimitiveType(kind); } @Override public NullType getNullType() { return javacTypes.getNullType(); } @Override public NoType getNoType(TypeKind kind) { return javacTypes.getNoType(kind); } @Override public ArrayType getArrayType(TypeMirror componentType) { if (isArtificialType(componentType)) { return new StandaloneArrayType(componentType); } return javacTypes.getArrayType(componentType); } @Override public WildcardType getWildcardType( @Nullable TypeMirror extendsBound, @Nullable TypeMirror superBound) { if (containsArtificialTypes(extendsBound, superBound)) { return new StandaloneWildcardType(extendsBound, superBound); } return javacTypes.getWildcardType(extendsBound, superBound); } @Override public DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs) { return getDeclaredType(null, typeElem, typeArgs); } @Override public DeclaredType getDeclaredType( @Nullable DeclaredType containing, TypeElement typeElem, TypeMirror... typeArgs) { if (isArtificialType(containing) || isArtificialElement(typeElem) || containsArtificialTypes(typeArgs)) { if (containing != null) { return new StandaloneDeclaredType(typeElem, Arrays.asList(typeArgs), containing); } else { return new StandaloneDeclaredType(this, typeElem, Arrays.asList(typeArgs)); } } return javacTypes.getDeclaredType(containing, typeElem, typeArgs); } @Nullable public TypeMirror getCanonicalType(@Nullable TypeMirror typeMirror) { if (typeMirror == null) { return null; } switch (typeMirror.getKind()) { case ARRAY: { ArrayType arrayType = (ArrayType) typeMirror; return getArrayType(getCanonicalType(arrayType.getComponentType())); } case TYPEVAR: { TypeVariable typeVar = (TypeVariable) typeMirror; return getElements().getCanonicalElement(typeVar.asElement()).asType(); } case WILDCARD: { WildcardType wildcardType = (WildcardType) typeMirror; return getWildcardType( getCanonicalType(wildcardType.getExtendsBound()), getCanonicalType(wildcardType.getSuperBound())); } case DECLARED: { DeclaredType declaredType = (DeclaredType) typeMirror; TypeMirror enclosingType = declaredType.getEnclosingType(); DeclaredType canonicalEnclosingType = enclosingType.getKind() != TypeKind.NONE ? (DeclaredType) getCanonicalType(enclosingType) : null; TypeElement canonicalElement = (TypeElement) getElements().getCanonicalElement(declaredType.asElement()); TypeMirror[] canonicalTypeArgs = declaredType .getTypeArguments() .stream() .map(this::getCanonicalType) .toArray(TypeMirror[]::new); return getDeclaredType(canonicalEnclosingType, canonicalElement, canonicalTypeArgs); } case PACKAGE: case ERROR: throw new UnsupportedOperationException("ErrorType NYI"); case BOOLEAN: case BYTE: case SHORT: case INT: case LONG: case CHAR: case FLOAT: case DOUBLE: case VOID: case NONE: case NULL: return typeMirror; case EXECUTABLE: case OTHER: case UNION: case INTERSECTION: default: throw new UnsupportedOperationException(); } } private boolean isArtificialElement(Element element) { return element instanceof TreeBackedElement; } private boolean isArtificialType(@Nullable TypeMirror typeMirror) { return typeMirror instanceof StandaloneTypeMirror; } private boolean containsArtificialTypes(TypeMirror... typeMirrors) { for (TypeMirror typeMirror : typeMirrors) { if (isArtificialType(typeMirror)) { return true; } } return false; } @Override public TypeMirror asMemberOf(DeclaredType containing, Element element) { throw new UnsupportedOperationException(); } }