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.impl; 27 28 import jdk.incubator.code.dialect.java.JavaOp; 29 import jdk.incubator.code.dialect.java.MethodRef; 30 import java.lang.invoke.MethodHandle; 31 import java.lang.invoke.MethodHandleInfo; 32 import java.lang.invoke.MethodHandles; 33 import java.lang.invoke.MethodType; 34 import java.lang.reflect.Method; 35 import jdk.incubator.code.dialect.core.FunctionType; 36 import jdk.incubator.code.dialect.java.JavaType; 37 import jdk.incubator.code.TypeElement; 38 import jdk.incubator.code.extern.ExternalizedTypeElement; 39 40 import java.util.List; 41 import java.util.function.Function; 42 43 import static java.util.stream.Collectors.joining; 44 45 public final class MethodRefImpl implements MethodRef { 46 47 final TypeElement refType; 48 final String name; 49 final FunctionType type; 50 51 public MethodRefImpl(TypeElement refType, String name, FunctionType type) { 52 this.refType = refType; 53 this.name = name; 54 this.type = type; 55 } 56 57 @Override 58 public TypeElement refType() { 59 return refType; 60 } 61 62 @Override 63 public String name() { 64 return name; 65 } 66 67 @Override 68 public FunctionType type() { 69 return type; 70 } 71 72 public Method resolveToDirectMethod(MethodHandles.Lookup l) throws ReflectiveOperationException { 73 return resolveToDirectHandle(l, hr -> hr.mhi().reflectAs(Method.class, l)); 74 } 75 76 public MethodHandle resolveToDirectHandle(MethodHandles.Lookup l) throws ReflectiveOperationException { 77 return resolveToDirectHandle(l, HandleResult::mh); 78 } 79 80 <T> T resolveToDirectHandle(MethodHandles.Lookup l, Function<HandleResult, T> f) throws ReflectiveOperationException { 81 ReflectiveOperationException roe = null; 82 for (JavaOp.InvokeOp.InvokeKind ik : 83 List.of(JavaOp.InvokeOp.InvokeKind.STATIC, JavaOp.InvokeOp.InvokeKind.INSTANCE)) { 84 try { 85 HandleResult hr = resolveToHandleResult(l, ik); 86 if (hr.isDirect()) { 87 return f.apply(hr); 88 } 89 } catch (NoSuchMethodException | IllegalAccessException e) { 90 roe = e; 91 } 92 } 93 if (roe == null) { 94 roe = new ReflectiveOperationException("Indirect reference to method"); 95 } 96 throw roe; 97 } 98 99 @Override 100 public Method resolveToMethod(MethodHandles.Lookup l, JavaOp.InvokeOp.InvokeKind kind) throws ReflectiveOperationException { 101 MethodHandleInfo methodHandleInfo = l.revealDirect(resolveToHandle(l, kind)); 102 return methodHandleInfo.reflectAs(Method.class, l); 103 } 104 105 @Override 106 public MethodHandle resolveToHandle(MethodHandles.Lookup l, JavaOp.InvokeOp.InvokeKind kind) throws ReflectiveOperationException { 107 Class<?> refC = resolve(l, refType); 108 MethodType mt = MethodRef.toNominalDescriptor(type).resolveConstantDesc(l); 109 return switch (kind) { 110 case SUPER -> l.findSpecial(refC, name, mt, l.lookupClass()); 111 case STATIC -> l.findStatic(refC, name, mt); 112 case INSTANCE -> l.findVirtual(refC, name, mt); 113 }; 114 } 115 116 record HandleResult (Class<?> refC, MethodHandle mh, MethodHandleInfo mhi) { 117 boolean isDirect() { 118 return refC == mhi.getDeclaringClass(); 119 } 120 } 121 122 HandleResult resolveToHandleResult(MethodHandles.Lookup l, JavaOp.InvokeOp.InvokeKind kind) throws ReflectiveOperationException { 123 Class<?> refC = resolve(l, refType); 124 MethodType mt = MethodRef.toNominalDescriptor(type).resolveConstantDesc(l); 125 MethodHandle mh = switch (kind) { 126 case SUPER -> l.findSpecial(refC, name, mt, l.lookupClass()); 127 case STATIC -> l.findStatic(refC, name, mt); 128 case INSTANCE -> l.findVirtual(refC, name, mt); 129 }; 130 MethodHandleInfo mhi = l.revealDirect(resolveToHandle(l, kind)); 131 return new HandleResult(refC, mh, mhi); 132 } 133 134 static Class<?> resolve(MethodHandles.Lookup l, TypeElement t) throws ReflectiveOperationException { 135 if (t instanceof JavaType jt) { 136 return (Class<?>)jt.erasure().resolve(l); 137 } else { 138 // @@@ 139 throw new ReflectiveOperationException(); 140 } 141 } 142 143 @Override 144 public ExternalizedTypeElement externalize() { 145 return JavaTypeUtils.methodRef(name, refType.externalize(), 146 type.returnType().externalize(), 147 type.parameterTypes().stream().map(TypeElement::externalize).toList()); 148 } 149 150 @Override 151 public String toString() { 152 return JavaTypeUtils.toExternalRefString(externalize()); 153 } 154 155 @Override 156 public boolean equals(Object o) { 157 if (this == o) return true; 158 if (o == null || getClass() != o.getClass()) return false; 159 160 MethodRefImpl that = (MethodRefImpl) o; 161 162 if (!refType.equals(that.refType)) return false; 163 if (!name.equals(that.name)) return false; 164 return type.equals(that.type); 165 } 166 167 @Override 168 public int hashCode() { 169 int result = refType.hashCode(); 170 result = 31 * result + name.hashCode(); 171 result = 31 * result + type.hashCode(); 172 return result; 173 } 174 }