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 
  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); break;
  76             case GETTER: validateFieldType(type, false, true); break;
  77             case SETTER: validateFieldType(type, true, true); break;
  78             case STATIC_GETTER: validateFieldType(type, false, false); break;
  79             case STATIC_SETTER: validateFieldType(type, true, false); break;
  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         switch (kind) {
 138             case VIRTUAL:
 139             case SPECIAL:
 140             case INTERFACE_VIRTUAL:
 141             case INTERFACE_SPECIAL:
 142                 return invocationType.dropParameterTypes(0, 1).descriptorString();
 143             case STATIC:
 144             case INTERFACE_STATIC:
 145                 return invocationType.descriptorString();
 146             case CONSTRUCTOR:
 147                 return invocationType.changeReturnType(CD_void).descriptorString();
 148             case GETTER:
 149             case STATIC_GETTER:
 150                 return invocationType.returnType().descriptorString();
 151             case SETTER:
 152                 return invocationType.parameterType(1).descriptorString();
 153             case STATIC_SETTER:
 154                 return invocationType.parameterType(0).descriptorString();
 155             default:
 156                 throw new IllegalStateException(kind.toString());
 157         }
 158     }
 159 
 160     public MethodHandle resolveConstantDesc(MethodHandles.Lookup lookup)
 161             throws ReflectiveOperationException {
 162         Class<?> resolvedOwner = (Class<?>) owner.resolveConstantDesc(lookup);
 163         MethodType invocationType = (MethodType) this.invocationType().resolveConstantDesc(lookup);
 164         switch (kind) {
 165             case STATIC:
 166             case INTERFACE_STATIC:
 167                 return lookup.findStatic(resolvedOwner, name, invocationType);
 168             case INTERFACE_VIRTUAL:
 169             case VIRTUAL:
 170                 return lookup.findVirtual(resolvedOwner, name, invocationType.dropParameterTypes(0, 1));
 171             case SPECIAL:
 172             case INTERFACE_SPECIAL:
 173                 return lookup.findSpecial(resolvedOwner, name, invocationType.dropParameterTypes(0, 1),
 174                                           lookup.lookupClass());
 175             case CONSTRUCTOR:
 176                 return lookup.findConstructor(resolvedOwner, invocationType.changeReturnType(void.class));
 177             case GETTER:
 178                 return lookup.findGetter(resolvedOwner, name, invocationType.returnType());
 179             case STATIC_GETTER:
 180                 return lookup.findStaticGetter(resolvedOwner, name, invocationType.returnType());
 181             case SETTER:
 182                 return lookup.findSetter(resolvedOwner, name, invocationType.parameterType(1));
 183             case STATIC_SETTER:
 184                 return lookup.findStaticSetter(resolvedOwner, name, invocationType.parameterType(0));
 185             default:
 186                 throw new IllegalStateException(kind.name());
 187         }
 188     }
 189 
 190     /**
 191      * Returns {@code true} if this {@linkplain DirectMethodHandleDescImpl} is
 192      * equal to another {@linkplain DirectMethodHandleDescImpl}.  Equality is
 193      * determined by the two descriptors having equal kind, owner, name, and type
 194      * descriptor.
 195      * @param o a {@code DirectMethodHandleDescImpl} to compare to this
 196      *       {@code DirectMethodHandleDescImpl}
 197      * @return {@code true} if the specified {@code DirectMethodHandleDescImpl} is
 198      *      equal to this {@code DirectMethodHandleDescImpl}.
 199      */
 200     @Override
 201     public boolean equals(Object o) {
 202         if (this == o) return true;
 203         if (o == null || getClass() != o.getClass()) return false;
 204         DirectMethodHandleDescImpl desc = (DirectMethodHandleDescImpl) o;
 205         return kind == desc.kind &&
 206                Objects.equals(owner, desc.owner) &&
 207                Objects.equals(name, desc.name) &&
 208                Objects.equals(invocationType, desc.invocationType);
 209     }
 210 
 211     @Override
 212     public int hashCode() {
 213         return Objects.hash(kind, owner, name, invocationType);
 214     }
 215 
 216     @Override
 217     public String toString() {
 218         return String.format("MethodHandleDesc[%s/%s::%s%s]", kind, owner.displayName(), name, invocationType.displayDescriptor());
 219     }
 220 }