1 /*
2 * Copyright (c) 2024, 2025, 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.ArrayType;
29 import jdk.incubator.code.dialect.java.JavaOp;
30 import jdk.incubator.code.dialect.java.JavaOp.InvokeOp.InvokeKind;
31 import jdk.incubator.code.dialect.java.MethodRef;
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.invoke.MethodType;
35 import java.lang.reflect.Array;
36 import java.lang.reflect.Constructor;
37 import java.lang.reflect.Method;
38 import jdk.incubator.code.dialect.core.FunctionType;
39 import jdk.incubator.code.TypeElement;
40 import jdk.incubator.code.extern.ExternalizedTypeElement;
41
42 public record MethodRefImpl(TypeElement refType, String name, FunctionType type) implements MethodRef {
43
44 static final MethodHandle MULTI_NEW_ARRAY_MH;
45
46 static {
47 try {
48 MULTI_NEW_ARRAY_MH = MethodHandles.lookup().findStatic(Array.class, "newInstance",
49 MethodType.methodType(Object.class, Class.class, int[].class));
50 } catch (ReflectiveOperationException ex) {
51 throw new ExceptionInInitializerError(ex);
52 }
53 }
54
55 @Override
56 public boolean isConstructor() {
57 return name.equals(INIT_NAME);
58 }
59
60 @Override
61 public Method resolveToMethod(MethodHandles.Lookup l) throws ReflectiveOperationException {
62 if (isConstructor()) {
63 throw new UnsupportedOperationException("Not a method reference");
64 }
65 MethodHandle mh = ResolutionHelper.resolveMethod(l, this);
66 return l.revealDirect(mh)
67 .reflectAs(Method.class, l);
68 }
69
70 @Override
71 public Constructor<?> resolveToConstructor(MethodHandles.Lookup l) throws ReflectiveOperationException {
72 if (!isConstructor()) {
73 throw new UnsupportedOperationException("Not a constructor reference");
74 }
75 return l.revealDirect(resolveToHandle(l, InvokeKind.SUPER))
76 .reflectAs(Constructor.class, l);
77 }
78
79 @Override
80 public MethodHandle resolveToHandle(MethodHandles.Lookup l, JavaOp.InvokeOp.InvokeKind kind) throws ReflectiveOperationException {
81 if (!isConstructor()) {
82 return ResolutionHelper.resolveMethod(l, this, kind);
83 } else {
84 if (kind != JavaOp.InvokeOp.InvokeKind.SUPER) {
85 throw new IllegalArgumentException("Bad invoke kind for constructor: " + kind);
86 }
87 return resolveToConstructorHandle(l);
88 }
89 }
90
91 private MethodHandle resolveToConstructorHandle(MethodHandles.Lookup l) throws ReflectiveOperationException {
92 Class<?> refC = ResolutionHelper.resolveClass(l, type.returnType());
93 if (type.returnType() instanceof ArrayType at) {
94 if (at.dimensions() == 1) {
95 return MethodHandles.arrayConstructor(refC);
96 } else {
97 int dims = type.parameterTypes().size();
98 Class<?> elementType = refC;
99 for (int i = 0 ; i < type.parameterTypes().size(); i++) {
100 elementType = elementType.componentType();
101 }
102 // only the use-site knows how many dimensions are specified
103 return MULTI_NEW_ARRAY_MH.asType(MULTI_NEW_ARRAY_MH.type().changeReturnType(refC))
104 .bindTo(elementType)
105 .asCollector(int[].class, dims);
106 }
107 } else {
108 // MH lookup wants a void-returning lookup type
109 MethodType mt = MethodRef.toNominalDescriptor(type).resolveConstantDesc(l).changeReturnType(void.class);
110 return l.findConstructor(refC, mt);
111 }
112 }
113
114 @Override
115 public ExternalizedTypeElement externalize() {
116 if (!isConstructor()) {
117 return JavaTypeUtils.methodRef(name, refType.externalize(),
118 type.returnType().externalize(),
119 type.parameterTypes().stream().map(TypeElement::externalize).toList());
120 } else {
121 return JavaTypeUtils.constructorRef(type.returnType().externalize(),
122 type.parameterTypes().stream().map(TypeElement::externalize).toList());
123 }
124 }
125
126 @Override
127 public String toString() {
128 return JavaTypeUtils.toExternalRefString(externalize());
129 }
130 }