1 /*
2  * Copyright (c) 2018, 2020, 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 package java.lang.constant;
26 
27 import java.lang.invoke.MethodHandle;
28 import java.lang.invoke.MethodHandles;
29 import java.lang.invoke.MethodType;
30 import java.util.Objects;
31 
32 import static java.lang.constant.ConstantDescs.CD_void;
33 import static java.lang.constant.ConstantUtils.validateClassOrInterface;
34 import static java.lang.constant.ConstantUtils.validateMemberName;
35 import static java.lang.constant.DirectMethodHandleDesc.Kind.CONSTRUCTOR;
36 import static java.util.Objects.requireNonNull;
37 
38 /**
39  * A <a href="package-summary.html#nominal">nominal descriptor</a> for a direct
40  * {@link MethodHandle}.  A {@linkplain DirectMethodHandleDescImpl} corresponds to
41  * a {@code Constant_MethodHandle_info} entry in the constant pool of a classfile.
42  */
43 final class DirectMethodHandleDescImpl implements DirectMethodHandleDesc {
44 
45     private final Kind kind;
46     private final ClassDesc owner;
47     private final String name;
48     private final MethodTypeDesc invocationType;
49 
50     /**
51      * Constructs a {@linkplain DirectMethodHandleDescImpl} for a method or field
52      * from a kind, owner, name, and type
53      *
54      * @param kind the kind of the method handle
55      * @param owner the declaring class or interface for the method
56      * @param name the unqualified name of the method (ignored if {@code kind} is {@code CONSTRUCTOR})
57      * @param type the lookup type of the method
58      * @throws NullPointerException if any non-ignored argument is null
59      * @throws IllegalArgumentException if {@code kind} describes a field accessor,
60      * and {@code type} is not consistent with that kind of field accessor, or if
61      * {@code kind} describes a constructor, and the return type of {@code type}
62      * is not {@code void}
63      * @jvms 4.2.2 Unqualified Names
64      */
DirectMethodHandleDescImpl(Kind kind, ClassDesc owner, String name, MethodTypeDesc type)65     DirectMethodHandleDescImpl(Kind kind, ClassDesc owner, String name, MethodTypeDesc type) {
66         if (kind == CONSTRUCTOR)
67             name = "<init>";
68 
69         requireNonNull(kind);
70         validateClassOrInterface(requireNonNull(owner));
71         validateMemberName(requireNonNull(name), true);
72         requireNonNull(type);
73 
74         switch (kind) {
75             case CONSTRUCTOR   -> validateConstructor(type);
76             case GETTER        -> validateFieldType(type, false, true);
77             case SETTER        -> validateFieldType(type, true, true);
78             case STATIC_GETTER -> validateFieldType(type, false, false);
79             case STATIC_SETTER -> validateFieldType(type, true, false);
80         }
81 
82         this.kind = kind;
83         this.owner = owner;
84         this.name = name;
85         if (kind.isVirtualMethod())
86             this.invocationType = type.insertParameterTypes(0, owner);
87         else if (kind == CONSTRUCTOR)
88             this.invocationType = type.changeReturnType(owner);
89         else
90             this.invocationType = type;
91     }
92 
validateFieldType(MethodTypeDesc type, boolean isSetter, boolean isVirtual)93     private static void validateFieldType(MethodTypeDesc type, boolean isSetter, boolean isVirtual) {
94         boolean isVoid = type.returnType().descriptorString().equals("V");
95         int expectedParams = (isSetter ? 1 : 0) + (isVirtual ? 1 : 0);
96         if (isVoid != isSetter
97             || type.parameterCount() != expectedParams
98             || (isVirtual && type.parameterType(0).isPrimitive())) {
99             String expectedType = String.format("(%s%s)%s", (isVirtual ? "R" : ""),
100                                                 (isSetter ? "T" : ""), (isSetter ? "V" : "T"));
101             throw new IllegalArgumentException(String.format("Expected type of %s for getter, found %s", expectedType, type));
102         }
103     }
104 
validateConstructor(MethodTypeDesc type)105     private static void validateConstructor(MethodTypeDesc type) {
106         if (!type.returnType().descriptorString().equals("V")) {
107             throw new IllegalArgumentException(String.format("Expected type of (T*)V for constructor, found %s", type));
108         }
109     }
110 
111     @Override
kind()112     public Kind kind() { return kind; }
113 
114     @Override
refKind()115     public int refKind() { return kind.refKind; }
116 
117     @Override
isOwnerInterface()118     public boolean isOwnerInterface() { return kind.isInterface; }
119 
120     @Override
owner()121     public ClassDesc owner() {
122         return owner;
123     }
124 
125     @Override
methodName()126     public String methodName() {
127         return name;
128     }
129 
130     @Override
invocationType()131     public MethodTypeDesc invocationType() {
132         return invocationType;
133     }
134 
135     @Override
lookupDescriptor()136     public String lookupDescriptor() {
137         return switch (kind) {
138             case VIRTUAL,
139                  SPECIAL,
140                  INTERFACE_VIRTUAL,
141                  INTERFACE_SPECIAL        -> invocationType.dropParameterTypes(0, 1).descriptorString();
142             case STATIC,
143                  INTERFACE_STATIC         -> invocationType.descriptorString();
144             case CONSTRUCTOR              -> invocationType.changeReturnType(CD_void).descriptorString();
145             case GETTER,
146                  STATIC_GETTER            -> invocationType.returnType().descriptorString();
147             case SETTER                   -> invocationType.parameterType(1).descriptorString();
148             case STATIC_SETTER            -> invocationType.parameterType(0).descriptorString();
149             default -> throw new IllegalStateException(kind.toString());
150         };
151     }
152 
resolveConstantDesc(MethodHandles.Lookup lookup)153     public MethodHandle resolveConstantDesc(MethodHandles.Lookup lookup)
154             throws ReflectiveOperationException {
155         Class<?> resolvedOwner = (Class<?>) owner.resolveConstantDesc(lookup);
156         MethodType invocationType = (MethodType) this.invocationType().resolveConstantDesc(lookup);
157         return switch (kind) {
158             case STATIC,
159                  INTERFACE_STATIC           -> lookup.findStatic(resolvedOwner, name, invocationType);
160             case VIRTUAL,
161                  INTERFACE_VIRTUAL          -> lookup.findVirtual(resolvedOwner, name, invocationType.dropParameterTypes(0, 1));
162             case SPECIAL,
163                  INTERFACE_SPECIAL          -> lookup.findSpecial(resolvedOwner, name, invocationType.dropParameterTypes(0, 1), lookup.lookupClass());
164             case CONSTRUCTOR                -> lookup.findConstructor(resolvedOwner, invocationType.changeReturnType(void.class));
165             case GETTER                     -> lookup.findGetter(resolvedOwner, name, invocationType.returnType());
166             case STATIC_GETTER              -> lookup.findStaticGetter(resolvedOwner, name, invocationType.returnType());
167             case SETTER                     -> lookup.findSetter(resolvedOwner, name, invocationType.parameterType(1));
168             case STATIC_SETTER              -> lookup.findStaticSetter(resolvedOwner, name, invocationType.parameterType(0));
169             default -> throw new IllegalStateException(kind.name());
170         };
171     }
172 
173     /**
174      * Returns {@code true} if this {@linkplain DirectMethodHandleDescImpl} is
175      * equal to another {@linkplain DirectMethodHandleDescImpl}.  Equality is
176      * determined by the two descriptors having equal kind, owner, name, and type
177      * descriptor.
178      * @param o a {@code DirectMethodHandleDescImpl} to compare to this
179      *       {@code DirectMethodHandleDescImpl}
180      * @return {@code true} if the specified {@code DirectMethodHandleDescImpl}
181      *      is equal to this {@code DirectMethodHandleDescImpl}.
182      */
183     @Override
184     public boolean equals(Object o) {
185         if (this == o) return true;
186         if (o == null || getClass() != o.getClass()) return false;
187         DirectMethodHandleDescImpl desc = (DirectMethodHandleDescImpl) o;
188         return kind == desc.kind &&
189                Objects.equals(owner, desc.owner) &&
190                Objects.equals(name, desc.name) &&
191                Objects.equals(invocationType, desc.invocationType);
192     }
193 
194     @Override
195     public int hashCode() {
196         return Objects.hash(kind, owner, name, invocationType);
197     }
198 
199     @Override
200     public String toString() {
201         return String.format("MethodHandleDesc[%s/%s::%s%s]", kind, owner.displayName(), name, invocationType.displayDescriptor());
202     }
203 }
204