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         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 
 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 
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
112     public Kind kind() { return kind; }
113 
114     @Override
115     public int refKind() { return kind.refKind; }
116 
117     @Override
118     public boolean isOwnerInterface() { return kind.isInterface; }
119 
120     @Override
121     public ClassDesc owner() {
122         return owner;
123     }
124 
125     @Override
126     public String methodName() {
127         return name;
128     }
129 
130     @Override
131     public MethodTypeDesc invocationType() {
132         return invocationType;
133     }
134 
135     @Override
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 
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 }
--- EOF ---