1 /*
   2  * Copyright (c) 2018, 2019, 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 import java.util.Optional;
  32 
  33 import static java.lang.constant.ConstantDescs.CD_void;
  34 import static java.lang.constant.ConstantUtils.validateClassOrInterface;
  35 import static java.lang.constant.ConstantUtils.validateMemberName;
  36 import static java.lang.constant.DirectMethodHandleDesc.Kind.CONSTRUCTOR;
  37 import static java.util.Objects.requireNonNull;
  38 
  39 /**
  40  * A <a href="package-summary.html#nominal">nominal descriptor</a> for a direct
  41  * {@link MethodHandle}.  A {@linkplain DirectMethodHandleDescImpl} corresponds to
  42  * a {@code Constant_MethodHandle_info} entry in the constant pool of a classfile.
  43  */
  44 final class DirectMethodHandleDescImpl implements DirectMethodHandleDesc {
  45 
  46     private final Kind kind;
  47     private final ClassDesc owner;
  48     private final String name;
  49     private final MethodTypeDesc invocationType;
  50 
  51     /**
  52      * Constructs a {@linkplain DirectMethodHandleDescImpl} for a method or field
  53      * from a kind, owner, name, and type
  54      *
  55      * @param kind the kind of the method handle
  56      * @param owner the declaring class or interface for the method
  57      * @param name the unqualified name of the method (ignored if {@code kind} is {@code CONSTRUCTOR})
  58      * @param type the lookup type of the method
  59      * @throws NullPointerException if any non-ignored argument is null
  60      * @throws IllegalArgumentException if {@code kind} describes a field accessor,
  61      * and {@code type} is not consistent with that kind of field accessor, or if
  62      * {@code kind} describes a constructor, and the return type of {@code type}
  63      * is not {@code void}
  64      * @jvms 4.2.2 Unqualified Names
  65      */
  66     DirectMethodHandleDescImpl(Kind kind, ClassDesc owner, String name, MethodTypeDesc type) {
  67         if (kind == CONSTRUCTOR)
  68             name = "<init>";
  69 
  70         requireNonNull(kind);
  71         validateClassOrInterface(requireNonNull(owner));
  72         validateMemberName(requireNonNull(name), true);
  73         requireNonNull(type);
  74 
  75         switch (kind) {
  76             case CONSTRUCTOR: validateConstructor(type); break;
  77             case GETTER: validateFieldType(type, false, true); break;
  78             case SETTER: validateFieldType(type, true, true); break;
  79             case STATIC_GETTER: validateFieldType(type, false, false); break;
  80             case STATIC_SETTER: validateFieldType(type, true, false); break;
  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 validateConstructor(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         switch (kind) {
 139             case VIRTUAL:
 140             case SPECIAL:
 141             case INTERFACE_VIRTUAL:
 142             case INTERFACE_SPECIAL:
 143                 return invocationType.dropParameterTypes(0, 1).descriptorString();
 144             case STATIC:
 145             case INTERFACE_STATIC:
 146                 return invocationType.descriptorString();
 147             case CONSTRUCTOR:
 148                 return invocationType.changeReturnType(CD_void).descriptorString();
 149             case GETTER:
 150             case STATIC_GETTER:
 151                 return invocationType.returnType().descriptorString();
 152             case SETTER:
 153                 return invocationType.parameterType(1).descriptorString();
 154             case STATIC_SETTER:
 155                 return invocationType.parameterType(0).descriptorString();
 156             default:
 157                 throw new IllegalStateException(kind.toString());
 158         }
 159     }
 160 
 161     public MethodHandle resolveConstantDesc(MethodHandles.Lookup lookup)
 162             throws ReflectiveOperationException {
 163         Class<?> resolvedOwner = (Class<?>) owner.resolveConstantDesc(lookup);
 164         MethodType invocationType = (MethodType) this.invocationType().resolveConstantDesc(lookup);
 165         switch (kind) {
 166             case STATIC:
 167             case INTERFACE_STATIC:
 168                 return lookup.findStatic(resolvedOwner, name, invocationType);
 169             case INTERFACE_VIRTUAL:
 170             case VIRTUAL:
 171                 return lookup.findVirtual(resolvedOwner, name, invocationType.dropParameterTypes(0, 1));
 172             case SPECIAL:
 173             case INTERFACE_SPECIAL:
 174                 return lookup.findSpecial(resolvedOwner, name, invocationType.dropParameterTypes(0, 1),
 175                                           lookup.lookupClass());
 176             case CONSTRUCTOR:
 177                 return lookup.findConstructor(resolvedOwner, invocationType.changeReturnType(void.class));
 178             case GETTER:
 179                 return lookup.findGetter(resolvedOwner, name, invocationType.returnType());
 180             case STATIC_GETTER:
 181                 return lookup.findStaticGetter(resolvedOwner, name, invocationType.returnType());
 182             case SETTER:
 183                 return lookup.findSetter(resolvedOwner, name, invocationType.parameterType(1));
 184             case STATIC_SETTER:
 185                 return lookup.findStaticSetter(resolvedOwner, name, invocationType.parameterType(0));
 186             default:
 187                 throw new IllegalStateException(kind.name());
 188         }
 189     }
 190 
 191     /**
 192      * Returns {@code true} if this {@linkplain DirectMethodHandleDescImpl} is
 193      * equal to another {@linkplain DirectMethodHandleDescImpl}.  Equality is
 194      * determined by the two descriptors having equal kind, owner, name, and type
 195      * descriptor.
 196      * @param o a {@code DirectMethodHandleDescImpl} to compare to this
 197      *       {@code DirectMethodHandleDescImpl}
 198      * @return {@code true} if the specified {@code DirectMethodHandleDescImpl} is
 199      *      equals to this {@code DirectMethodHandleDescImpl}.
 200      */
 201     @Override
 202     public boolean equals(Object o) {
 203         if (this == o) return true;
 204         if (o == null || getClass() != o.getClass()) return false;
 205         DirectMethodHandleDescImpl desc = (DirectMethodHandleDescImpl) o;
 206         return kind == desc.kind &&
 207                Objects.equals(owner, desc.owner) &&
 208                Objects.equals(name, desc.name) &&
 209                Objects.equals(invocationType, desc.invocationType);
 210     }
 211 
 212     @Override
 213     public int hashCode() {
 214         return Objects.hash(kind, owner, name, invocationType);
 215     }
 216 
 217     @Override
 218     public String toString() {
 219         return String.format("MethodHandleDesc[%s/%s::%s%s]", kind, owner.displayName(), name, invocationType.displayDescriptor());
 220     }
 221 
 222     @Override
 223     public Optional<? extends ConstantDesc> describeConstable() {
 224         return null;
 225     }
 226 }