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 }