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      */
 65     DirectMethodHandleDescImpl(Kind kind, ClassDesc owner, String name, MethodTypeDesc type) {
 66         if (kind == CONSTRUCTOR) {
 67             name = "<init>";
 68         }
 69 
 70         requireNonNull(kind);
 71         validateClassOrInterface(requireNonNull(owner));
 72         validateMemberName(requireNonNull(name), true);
 73         requireNonNull(type);
 74 
 75         switch (kind) {
 76             case CONSTRUCTOR   -> validateObjectConstructor(type);
 77             case GETTER        -> validateFieldType(type, false, true);
 78             case SETTER        -> validateFieldType(type, true, true);
 79             case STATIC_GETTER -> validateFieldType(type, false, false);
 80             case STATIC_SETTER -> validateFieldType(type, true, false);
 81         }
 82 
 83         this.kind = kind;
 84         this.owner = owner;
 85         this.name = name;
 86         if (kind.isVirtualMethod())
 87             this.invocationType = type.insertParameterTypes(0, owner);
 88         else if (kind == CONSTRUCTOR)
 89             this.invocationType = type.changeReturnType(owner);
 90         else
 91             this.invocationType = type;
 92     }
 93 
 94     private static void validateFieldType(MethodTypeDesc type, boolean isSetter, boolean isVirtual) {
 95         boolean isVoid = type.returnType().descriptorString().equals("V");
 96         int expectedParams = (isSetter ? 1 : 0) + (isVirtual ? 1 : 0);
 97         if (isVoid != isSetter
 98             || type.parameterCount() != expectedParams
 99             || (isVirtual && type.parameterType(0).isPrimitive())) {
100             String expectedType = String.format("(%s%s)%s", (isVirtual ? "R" : ""),
101                                                 (isSetter ? "T" : ""), (isSetter ? "V" : "T"));
102             throw new IllegalArgumentException(String.format("Expected type of %s for getter, found %s", expectedType, type));
103         }
104     }
105 
106     private static void validateObjectConstructor(MethodTypeDesc type) {
107         if (!type.returnType().descriptorString().equals("V")) {
108             throw new IllegalArgumentException(String.format("Expected type of (T*)V for constructor, found %s", type));
109         }
110     }
111 
112     @Override
113     public Kind kind() { return kind; }
114 
115     @Override
116     public int refKind() { return kind.refKind; }
117 
118     @Override
119     public boolean isOwnerInterface() { return kind.isInterface; }
120 
121     @Override
122     public ClassDesc owner() {
123         return owner;
124     }
125 
126     @Override
127     public String methodName() {
128         return name;
129     }
130 
131     @Override
132     public MethodTypeDesc invocationType() {
133         return invocationType;
134     }
135 
136     @Override
137     public String lookupDescriptor() {
138         return switch (kind) {
139             case VIRTUAL,
140                  SPECIAL,
141                  INTERFACE_VIRTUAL,
142                  INTERFACE_SPECIAL        -> invocationType.dropParameterTypes(0, 1).descriptorString();
143             case STATIC,
144                  INTERFACE_STATIC         -> invocationType.descriptorString();
145             case CONSTRUCTOR              -> invocationType.changeReturnType(CD_void).descriptorString();
146             case GETTER,
147                  STATIC_GETTER            -> invocationType.returnType().descriptorString();
148             case SETTER                   -> invocationType.parameterType(1).descriptorString();
149             case STATIC_SETTER            -> invocationType.parameterType(0).descriptorString();
150             default -> throw new IllegalStateException(kind.toString());
151         };
152     }
153 
154     public MethodHandle resolveConstantDesc(MethodHandles.Lookup lookup)
155             throws ReflectiveOperationException {
156         Class<?> resolvedOwner = (Class<?>) owner.resolveConstantDesc(lookup);
157         MethodType invocationType = (MethodType) this.invocationType().resolveConstantDesc(lookup);
158         return switch (kind) {
159             case STATIC,
160                  INTERFACE_STATIC           -> lookup.findStatic(resolvedOwner, name, invocationType);
161             case VIRTUAL,
162                  INTERFACE_VIRTUAL          -> lookup.findVirtual(resolvedOwner, name, invocationType.dropParameterTypes(0, 1));
163             case SPECIAL,
164                  INTERFACE_SPECIAL          -> lookup.findSpecial(resolvedOwner, name, invocationType.dropParameterTypes(0, 1), lookup.lookupClass());
165             case CONSTRUCTOR                -> lookup.findConstructor(resolvedOwner, invocationType.changeReturnType(void.class));
166             case GETTER                     -> lookup.findGetter(resolvedOwner, name, invocationType.returnType());
167             case STATIC_GETTER              -> lookup.findStaticGetter(resolvedOwner, name, invocationType.returnType());
168             case SETTER                     -> lookup.findSetter(resolvedOwner, name, invocationType.parameterType(1));
169             case STATIC_SETTER              -> lookup.findStaticSetter(resolvedOwner, name, invocationType.parameterType(0));
170             default -> throw new IllegalStateException(kind.name());
171         };
172     }
173 
174     /**
175      * Returns {@code true} if this {@linkplain DirectMethodHandleDescImpl} is
176      * equal to another {@linkplain DirectMethodHandleDescImpl}.  Equality is
177      * determined by the two descriptors having equal kind, owner, name, and type
178      * descriptor.
179      * @param o a {@code DirectMethodHandleDescImpl} to compare to this
180      *       {@code DirectMethodHandleDescImpl}
181      * @return {@code true} if the specified {@code DirectMethodHandleDescImpl}
182      *      is equal to this {@code DirectMethodHandleDescImpl}.
183      */
184     @Override
185     public boolean equals(Object o) {
186         if (this == o) return true;
187         if (o == null || getClass() != o.getClass()) return false;
188         DirectMethodHandleDescImpl desc = (DirectMethodHandleDescImpl) o;
189         return kind == desc.kind &&
190                Objects.equals(owner, desc.owner) &&
191                Objects.equals(name, desc.name) &&
192                Objects.equals(invocationType, desc.invocationType);
193     }
194 
195     @Override
196     public int hashCode() {
197         return Objects.hash(kind, owner, name, invocationType);
198     }
199 
200     @Override
201     public String toString() {
202         return String.format("MethodHandleDesc[%s/%s::%s%s]", kind, owner.displayName(), name, invocationType.displayDescriptor());
203     }
204 }