1 /*
   2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   3  *
   4  * This code is free software; you can redistribute it and/or modify it
   5  * under the terms of the GNU General Public License version 2 only, as
   6  * published by the Free Software Foundation.  Oracle designates this
   7  * particular file as subject to the "Classpath" exception as provided
   8  * by Oracle in the LICENSE file that accompanied this code.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 /*
  26  * This file is available under and governed by the GNU General Public
  27  * License version 2 only, as published by the Free Software Foundation.
  28  * However, the following notice accompanied the original version of this
  29  * file:
  30  *
  31  * ASM: a very small and fast Java bytecode manipulation framework
  32  * Copyright (c) 2000-2011 INRIA, France Telecom
  33  * All rights reserved.
  34  *
  35  * Redistribution and use in source and binary forms, with or without
  36  * modification, are permitted provided that the following conditions
  37  * are met:
  38  * 1. Redistributions of source code must retain the above copyright
  39  *    notice, this list of conditions and the following disclaimer.
  40  * 2. Redistributions in binary form must reproduce the above copyright
  41  *    notice, this list of conditions and the following disclaimer in the
  42  *    documentation and/or other materials provided with the distribution.
  43  * 3. Neither the name of the copyright holders nor the names of its
  44  *    contributors may be used to endorse or promote products derived from
  45  *    this software without specific prior written permission.
  46  *
  47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  48  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  50  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  51  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  57  * THE POSSIBILITY OF SUCH DAMAGE.
  58  */
  59 package jdk.internal.org.objectweb.asm.commons;
  60 
  61 import java.util.HashMap;
  62 import java.util.Map;
  63 import jdk.internal.org.objectweb.asm.Type;
  64 
  65 /**
  66  * A named method descriptor.
  67  *
  68  * @author Juozas Baliuka
  69  * @author Chris Nokleberg
  70  * @author Eric Bruneton
  71  */
  72 public class Method {
  73 
  74     /** The method name. */
  75     private final String name;
  76 
  77     /** The method descriptor. */
  78     private final String descriptor;
  79 
  80     /** The descriptors of the primitive Java types (plus void). */
  81     private static final Map<String, String> PRIMITIVE_TYPE_DESCRIPTORS;
  82 
  83     static {
  84         HashMap<String, String> descriptors = new HashMap<String, String>();
  85         descriptors.put("void", "V");
  86         descriptors.put("byte", "B");
  87         descriptors.put("char", "C");
  88         descriptors.put("double", "D");
  89         descriptors.put("float", "F");
  90         descriptors.put("int", "I");
  91         descriptors.put("long", "J");
  92         descriptors.put("short", "S");
  93         descriptors.put("boolean", "Z");
  94         PRIMITIVE_TYPE_DESCRIPTORS = descriptors;
  95     }
  96 
  97     /**
  98       * Constructs a new {@link Method}.
  99       *
 100       * @param name the method's name.
 101       * @param descriptor the method's descriptor.
 102       */
 103     public Method(final String name, final String descriptor) {
 104         this.name = name;
 105         this.descriptor = descriptor;
 106     }
 107 
 108     /**
 109       * Constructs a new {@link Method}.
 110       *
 111       * @param name the method's name.
 112       * @param returnType the method's return type.
 113       * @param argumentTypes the method's argument types.
 114       */
 115     public Method(final String name, final Type returnType, final Type[] argumentTypes) {
 116         this(name, Type.getMethodDescriptor(returnType, argumentTypes));
 117     }
 118 
 119     /**
 120       * Creates a new {@link Method}.
 121       *
 122       * @param method a java.lang.reflect method descriptor
 123       * @return a {@link Method} corresponding to the given Java method declaration.
 124       */
 125     public static Method getMethod(final java.lang.reflect.Method method) {
 126         return new Method(method.getName(), Type.getMethodDescriptor(method));
 127     }
 128 
 129     /**
 130       * Creates a new {@link Method}.
 131       *
 132       * @param constructor a java.lang.reflect constructor descriptor
 133       * @return a {@link Method} corresponding to the given Java constructor declaration.
 134       */
 135     public static Method getMethod(final java.lang.reflect.Constructor<?> constructor) {
 136         return new Method("<init>", Type.getConstructorDescriptor(constructor));
 137     }
 138 
 139     /**
 140       * Returns a {@link Method} corresponding to the given Java method declaration.
 141       *
 142       * @param method a Java method declaration, without argument names, of the form "returnType name
 143       *     (argumentType1, ... argumentTypeN)", where the types are in plain Java (e.g. "int",
 144       *     "float", "java.util.List", ...). Classes of the java.lang package can be specified by their
 145       *     unqualified name; all other classes names must be fully qualified.
 146       * @return a {@link Method} corresponding to the given Java method declaration.
 147       * @throws IllegalArgumentException if <code>method</code> could not get parsed.
 148       */
 149     public static Method getMethod(final String method) {
 150         return getMethod(method, false);
 151     }
 152 
 153     /**
 154       * Returns a {@link Method} corresponding to the given Java method declaration.
 155       *
 156       * @param method a Java method declaration, without argument names, of the form "returnType name
 157       *     (argumentType1, ... argumentTypeN)", where the types are in plain Java (e.g. "int",
 158       *     "float", "java.util.List", ...). Classes of the java.lang package may be specified by their
 159       *     unqualified name, depending on the defaultPackage argument; all other classes names must be
 160       *     fully qualified.
 161       * @param defaultPackage true if unqualified class names belong to the default package, or false
 162       *     if they correspond to java.lang classes. For instance "Object" means "Object" if this
 163       *     option is true, or "java.lang.Object" otherwise.
 164       * @return a {@link Method} corresponding to the given Java method declaration.
 165       * @throws IllegalArgumentException if <code>method</code> could not get parsed.
 166       */
 167     public static Method getMethod(final String method, final boolean defaultPackage) {
 168         final int spaceIndex = method.indexOf(' ');
 169         int currentArgumentStartIndex = method.indexOf('(', spaceIndex) + 1;
 170         final int endIndex = method.indexOf(')', currentArgumentStartIndex);
 171         if (spaceIndex == -1 || currentArgumentStartIndex == 0 || endIndex == -1) {
 172             throw new IllegalArgumentException();
 173         }
 174         final String returnType = method.substring(0, spaceIndex);
 175         final String methodName =
 176                 method.substring(spaceIndex + 1, currentArgumentStartIndex - 1).trim();
 177         StringBuilder stringBuilder = new StringBuilder();
 178         stringBuilder.append('(');
 179         int currentArgumentEndIndex;
 180         do {
 181             String argumentDescriptor;
 182             currentArgumentEndIndex = method.indexOf(',', currentArgumentStartIndex);
 183             if (currentArgumentEndIndex == -1) {
 184                 argumentDescriptor =
 185                         getDescriptorInternal(
 186                                 method.substring(currentArgumentStartIndex, endIndex).trim(), defaultPackage);
 187             } else {
 188                 argumentDescriptor =
 189                         getDescriptorInternal(
 190                                 method.substring(currentArgumentStartIndex, currentArgumentEndIndex).trim(),
 191                                 defaultPackage);
 192                 currentArgumentStartIndex = currentArgumentEndIndex + 1;
 193             }
 194             stringBuilder.append(argumentDescriptor);
 195         } while (currentArgumentEndIndex != -1);
 196         stringBuilder.append(')').append(getDescriptorInternal(returnType, defaultPackage));
 197         return new Method(methodName, stringBuilder.toString());
 198     }
 199 
 200     /**
 201       * Returns the descriptor corresponding to the given type name.
 202       *
 203       * @param type a Java type name.
 204       * @param defaultPackage true if unqualified class names belong to the default package, or false
 205       *     if they correspond to java.lang classes. For instance "Object" means "Object" if this
 206       *     option is true, or "java.lang.Object" otherwise.
 207       * @return the descriptor corresponding to the given type name.
 208       */
 209     private static String getDescriptorInternal(final String type, final boolean defaultPackage) {
 210         if ("".equals(type)) {
 211             return type;
 212         }
 213 
 214         StringBuilder stringBuilder = new StringBuilder();
 215         int arrayBracketsIndex = 0;
 216         while ((arrayBracketsIndex = type.indexOf("[]", arrayBracketsIndex) + 1) > 0) {
 217             stringBuilder.append('[');
 218         }
 219 
 220         String elementType = type.substring(0, type.length() - stringBuilder.length() * 2);
 221         String descriptor = PRIMITIVE_TYPE_DESCRIPTORS.get(elementType);
 222         if (descriptor != null) {
 223             stringBuilder.append(descriptor);
 224         } else {
 225             // FIXME: support Q-type
 226             stringBuilder.append('L');
 227             if (elementType.indexOf('.') < 0) {
 228                 if (!defaultPackage) {
 229                     stringBuilder.append("java/lang/");
 230                 }
 231                 stringBuilder.append(elementType);
 232             } else {
 233                 stringBuilder.append(elementType.replace('.', '/'));
 234             }
 235             stringBuilder.append(';');
 236         }
 237         return stringBuilder.toString();
 238     }
 239 
 240     /**
 241       * Returns the name of the method described by this object.
 242       *
 243       * @return the name of the method described by this object.
 244       */
 245     public String getName() {
 246         return name;
 247     }
 248 
 249     /**
 250       * Returns the descriptor of the method described by this object.
 251       *
 252       * @return the descriptor of the method described by this object.
 253       */
 254     public String getDescriptor() {
 255         return descriptor;
 256     }
 257 
 258     /**
 259       * Returns the return type of the method described by this object.
 260       *
 261       * @return the return type of the method described by this object.
 262       */
 263     public Type getReturnType() {
 264         return Type.getReturnType(descriptor);
 265     }
 266 
 267     /**
 268       * Returns the argument types of the method described by this object.
 269       *
 270       * @return the argument types of the method described by this object.
 271       */
 272     public Type[] getArgumentTypes() {
 273         return Type.getArgumentTypes(descriptor);
 274     }
 275 
 276     @Override
 277     public String toString() {
 278         return name + descriptor;
 279     }
 280 
 281     @Override
 282     public boolean equals(final Object other) {
 283         if (!(other instanceof Method)) {
 284             return false;
 285         }
 286         Method otherMethod = (Method) other;
 287         return name.equals(otherMethod.name) && descriptor.equals(otherMethod.descriptor);
 288     }
 289 
 290     @Override
 291     public int hashCode() {
 292         return name.hashCode() ^ descriptor.hashCode();
 293     }
 294 }