1 /*
  2  * Copyright (c) 2024, 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 jdk.incubator.code.dialect.java;
 27 
 28 import jdk.incubator.code.TypeElement;
 29 import jdk.incubator.code.dialect.java.JavaOp.InvokeOp.InvokeKind;
 30 import jdk.incubator.code.dialect.java.impl.JavaTypeUtils;
 31 import jdk.incubator.code.dialect.java.impl.MethodRefImpl;
 32 import jdk.incubator.code.extern.ExternalizedTypeElement;
 33 
 34 import java.lang.constant.ClassDesc;
 35 import java.lang.invoke.MethodHandles.Lookup;
 36 import java.lang.reflect.Constructor;
 37 import java.lang.reflect.Method;
 38 import java.lang.reflect.Type;
 39 import java.lang.reflect.TypeVariable;
 40 import java.util.Objects;
 41 
 42 /**
 43  * A type variable type
 44  */
 45 public final class TypeVariableType implements JavaType {
 46 
 47     final String name;
 48     final Owner owner;
 49     final JavaType bound;
 50 
 51     TypeVariableType(String name, Owner owner, JavaType bound) {
 52         this.name = name;
 53         this.owner = owner;
 54         this.bound = bound;
 55     }
 56 
 57     @Override
 58     public Type resolve(Lookup lookup) throws ReflectiveOperationException {
 59         TypeVariable<?>[] typeVariables = switch (owner) {
 60             case MethodRef constructorRef when constructorRef.isConstructor() -> {
 61                 Constructor<?> constructor = constructorRef.resolveToConstructor(lookup);
 62                 yield constructor.getTypeParameters();
 63             }
 64             case MethodRef methodRef -> {
 65                 Method method = methodRef.resolveToMethod(lookup);
 66                 yield method.getTypeParameters();
 67             }
 68             case JavaType type -> {
 69                 Class<?> erasedDecl = (Class<?>)type.resolve(lookup);
 70                 yield erasedDecl.getTypeParameters();
 71             }
 72         };
 73         for (TypeVariable<?> tv : typeVariables) {
 74             if (tv.getName().equals(name)) {
 75                 return tv;
 76             }
 77         }
 78         throw new ReflectiveOperationException("Type-variable not found: " + name);
 79     }
 80 
 81     /**
 82      * {@return the type-variable name}
 83      */
 84     public String name() {
 85         return name;
 86     }
 87 
 88     /**
 89      * {@return the type-variable bound}
 90      */
 91     public JavaType bound() {
 92         return bound;
 93     }
 94 
 95     /**
 96      * {@return the owner of this type-variable}
 97      */
 98     public Owner owner() {
 99         return owner;
100     }
101 
102     @Override
103     public JavaType erasure() {
104         return bound.erasure();
105     }
106 
107     @Override
108     public ExternalizedTypeElement externalize() {
109         return JavaTypeUtils.typeVarType(name, owner.externalize(), bound.externalize());
110     }
111 
112     @Override
113     public String toString() {
114         return JavaTypeUtils.toExternalTypeString(externalize());
115     }
116 
117     @Override
118     public boolean equals(Object o) {
119         if (this == o) return true;
120         return o instanceof TypeVariableType that &&
121                 Objects.equals(name, that.name) &&
122                 Objects.equals(owner, that.owner) &&
123                 Objects.equals(bound, that.bound);
124     }
125 
126     @Override
127     public int hashCode() {
128         return Objects.hash(name, owner, bound);
129     }
130 
131     @Override
132     public JavaType toBasicType() {
133         return erasure().toBasicType();
134     }
135 
136     @Override
137     public ClassDesc toNominalDescriptor() {
138         return erasure().toNominalDescriptor();
139     }
140 
141     /**
142      * The owner of a type variable, either a class or a method.
143      */
144     public sealed interface Owner extends TypeElement permits ClassType, MethodRef { }
145 }