/* * Copyright 2017-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; import com.google.common.base.Preconditions; import java.util.stream.Stream; import javax.annotation.Nullable; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.IntersectionType; import javax.lang.model.type.NoType; 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.util.Elements; import javax.lang.model.util.SimpleElementVisitor8; import javax.lang.model.util.SimpleTypeVisitor8; import org.objectweb.asm.Type; /** Computes type descriptors from {@link Element}s or {@link TypeMirror}s. */ class DescriptorFactory { private final Elements elements; public DescriptorFactory(Elements elements) { this.elements = elements; } public String getDescriptor(TypeMirror type) { return getType(type).getDescriptor(); } @Nullable public String getDescriptor(Element element) { ElementKind kind = element.getKind(); if (kind.isClass() || kind.isInterface()) { return null; } return getType(element).getDescriptor(); } private Type getType(Element element) { return Preconditions.checkNotNull( element.accept( new SimpleElementVisitor8<Type, Void>() { @Override protected Type defaultAction(Element e, Void aVoid) { throw new IllegalArgumentException( String.format("Unexpected element kind: %s", element.getKind())); } @Override public Type visitExecutable(ExecutableElement e, Void aVoid) { Stream<TypeMirror> parameterTypeStream = e.getParameters().stream().map(Element::asType); if (MoreElements.isInnerClassConstructor(e)) { parameterTypeStream = Stream.concat( Stream.of(MoreElements.getOuterClass(e).asType()), parameterTypeStream); } return Type.getMethodType( getType(e.getReturnType()), parameterTypeStream .map(DescriptorFactory.this::getType) .toArray(size -> new Type[size])); } @Override public Type visitVariable(VariableElement e, Void aVoid) { return getType(e.asType()); } }, null)); } public Type getType(TypeMirror typeMirror) { return Preconditions.checkNotNull( typeMirror.accept( new SimpleTypeVisitor8<Type, Void>() { @Override protected Type defaultAction(TypeMirror t, Void aVoid) { throw new IllegalArgumentException( String.format("Unexpected type kind: %s", t.getKind())); } @Override public Type visitPrimitive(PrimitiveType type, Void aVoid) { switch (type.getKind()) { case BOOLEAN: return Type.BOOLEAN_TYPE; case BYTE: return Type.BYTE_TYPE; case CHAR: return Type.CHAR_TYPE; case SHORT: return Type.SHORT_TYPE; case INT: return Type.INT_TYPE; case LONG: return Type.LONG_TYPE; case FLOAT: return Type.FLOAT_TYPE; case DOUBLE: return Type.DOUBLE_TYPE; // $CASES-OMITTED$ default: throw new IllegalArgumentException( String.format("Unexpected type kind: %s", type.getKind())); } } @Override public Type visitNoType(NoType t, Void aVoid) { if (t.getKind() != TypeKind.VOID) { throw new IllegalArgumentException( String.format("Unexpected type kind: %s", t.getKind())); } return Type.VOID_TYPE; } @Override public Type visitArray(ArrayType t, Void aVoid) { return Type.getObjectType("[" + getDescriptor(t.getComponentType())); } @Override public Type visitDeclared(DeclaredType t, Void aVoid) { // The erasure of a parameterized type is just the unparameterized version (JLS8 4.6) return Type.getObjectType(getInternalName((TypeElement) t.asElement())); } @Override public Type visitTypeVariable(TypeVariable t, Void aVoid) { // The erasure of a type variable is the erasure of its leftmost bound (JLS8 4.6) // If there's only one bound, getUpperBound returns it directly; if there's more than // one it returns an IntersectionType return getType(t.getUpperBound()); } @Override public Type visitIntersection(IntersectionType t, Void aVoid) { // The erasure of a type variable is the erasure of its leftmost bound (JLS8 4.6) return getType(t.getBounds().get(0)); } }, null)); } /** Gets the internal form of the binary name; see JVMS8 4.2.1 */ public String getInternalName(TypeMirror typeMirror) { return getType(typeMirror).getInternalName(); } /** Gets the internal form of the binary name; see JVMS8 4.2.1 */ public String getInternalName(TypeElement typeElement) { return elements.getBinaryName(typeElement).toString().replace('.', '/'); } }