1 /*
   2  * Copyright (c) 2018, 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.util.ArrayList;
  28 import java.util.List;
  29 import java.util.NoSuchElementException;
  30 import java.util.Optional;
  31 import java.util.Set;
  32 
  33 import static java.util.Objects.requireNonNull;
  34 
  35 /**
  36  * Helper methods for the implementation of {@code java.lang.constant}.
  37  */
  38 class ConstantUtils {
  39     /** an empty constant descriptor */
  40     public static final ConstantDesc<?>[] EMPTY_CONSTANTDESC = new ConstantDesc<?>[0];
  41     static final Constable<?>[] EMPTY_CONSTABLE = new Constable<?>[0];
  42 
  43     private static final Set<String> pointyNames = Set.of("<init>", "<clinit>");
  44 
  45     static String validateBinaryClassName(String name) {
  46         for (int i=0; i<name.length(); i++) {
  47             char ch = name.charAt(i);
  48             if (ch == ';' || ch == '[' || ch == '/')
  49                 throw new IllegalArgumentException("Invalid class name: " + name);
  50         }
  51         return name;
  52     }
  53 
  54     /**
  55      * validates a member name
  56      * @param name the name of the member
  57      * @return the name passed if valid
  58      */
  59     public static String validateMemberName(String name) {
  60         requireNonNull(name);
  61         if (name.length() == 0)
  62             throw new IllegalArgumentException("zero-length member name");
  63         for (int i=0; i<name.length(); i++) {
  64             char ch = name.charAt(i);
  65             if (ch == '.' || ch == ';' || ch == '[' || ch == '/')
  66                 throw new IllegalArgumentException("Invalid member name: " + name);
  67             if (ch == '<' || ch == '>') {
  68                 if (!pointyNames.contains(name))
  69                     throw new IllegalArgumentException("Invalid member name: " + name);
  70             }
  71         }
  72         return name;
  73     }
  74 
  75     static void validateClassOrInterface(ClassDesc clazz) {
  76         if (!clazz.isClassOrInterface())
  77             throw new IllegalArgumentException("not a class or interface type: " + clazz);
  78     }
  79 
  80     static int arrayDepth(String descriptorString) {
  81         int depth = 0;
  82         while (descriptorString.charAt(depth) == '[')
  83             depth++;
  84         return depth;
  85     }
  86 
  87     static String binaryToInternal(String name) {
  88         return name.replace('.', '/');
  89     }
  90 
  91     static String internalToBinary(String name) {
  92         return name.replace('/', '.');
  93     }
  94 
  95     static String dropLastChar(String s) {
  96         return s.substring(0, s.length() - 1);
  97     }
  98 
  99     static String dropFirstAndLastChar(String s) {
 100         return s.substring(1, s.length() - 1);
 101     }
 102 
 103     /**
 104      * Produce an {@code Optional<DynamicConstantDesc<T>>} describing the invocation
 105      * of the specified bootstrap with the specified arguments.  The arguments will
 106      * be converted to nominal descriptors using the provided lookup.  Helper
 107      * method for implementing {@link Constable#describeConstable()}.
 108      *
 109      * @param <T> the type of the resulting constant
 110      * @param bootstrap nominal descriptor for the bootstrap method
 111      * @param type nominal descriptor for the type of the resulting constant
 112      * @param args nominal descriptors for the bootstrap arguments
 113      * @return the nominal descriptor for the dynamic constant
 114      */
 115     public static<T> Optional<DynamicConstantDesc<T>> symbolizeHelper(MethodHandleDesc bootstrap,
 116                                                                       ClassDesc type,
 117                                                                       Constable<?>... args) {
 118         requireNonNull(bootstrap);
 119         requireNonNull(type);
 120         requireNonNull(args);
 121         try {
 122             ConstantDesc<?>[] quotedArgs = new ConstantDesc<?>[args.length + 1];
 123             quotedArgs[0] = bootstrap;
 124             for (int i=0; i<args.length; i++)
 125                 quotedArgs[i+1] = args[i].describeConstable().orElseThrow();
 126             return Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE,
 127                                                            ConstantDescs.DEFAULT_NAME,
 128                                                            type, quotedArgs));
 129         }
 130         catch (NoSuchElementException e) {
 131             return Optional.empty();
 132         }
 133     }
 134 
 135     /**
 136      * Parse a method descriptor string, and return a list of field descriptor
 137      * strings, return type first, then parameter types
 138      *
 139      * @param descriptor the descriptor string
 140      * @return the list of types
 141      * @throws IllegalArgumentException if the descriptor string is not valid
 142      */
 143     static List<String> parseMethodDescriptor(String descriptor) {
 144         int cur = 0, end = descriptor.length();
 145         ArrayList<String> ptypes = new ArrayList<>();
 146 
 147         if (cur >= end || descriptor.charAt(cur) != '(')
 148             throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
 149 
 150         ++cur;  // skip '('
 151         while (cur < end && descriptor.charAt(cur) != ')') {
 152             int len = matchSig(descriptor, cur, end);
 153             if (len == 0 || descriptor.charAt(cur) == 'V')
 154                 throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
 155             ptypes.add(descriptor.substring(cur, cur + len));
 156             cur += len;
 157         }
 158         if (cur >= end)
 159             throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
 160         ++cur;  // skip ')'
 161 
 162         int rLen = matchSig(descriptor, cur, end);
 163         if (rLen == 0 || cur + rLen != end)
 164             throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
 165         ptypes.add(0, descriptor.substring(cur, cur + rLen));
 166         return ptypes;
 167     }
 168 
 169     /**
 170      * Validate that the characters at [start, end) within the provided string
 171      * describe a valid field type descriptor.
 172      *
 173      * @param str the descriptor string
 174      * @param start the starting index into the string
 175      * @param end the ending index within the string
 176      * @return the length of the descriptor, or 0 if it is not a descriptor
 177      * @throws IllegalArgumentException if the descriptor string is not valid
 178      */
 179     static int matchSig(String str, int start, int end) {
 180         if (start >= end || start >= str.length() || end > str.length())
 181             return 0;
 182         char c = str.charAt(start);
 183         if (c == 'L') {
 184             int endc = str.indexOf(';', start);
 185             int badc = str.indexOf('.', start);
 186             if (badc >= 0 && badc < endc)
 187                 return 0;
 188             badc = str.indexOf('[', start);
 189             if (badc >= 0 && badc < endc)
 190                 return 0;
 191             return (endc < 0) ? 0 : endc - start + 1;
 192         } else if (c == '[') {
 193             int t = matchSig(str, start+1, end);
 194             return (t > 0) ? t + 1 : 0;
 195         } else {
 196             return ("IJCSBFDZV".indexOf(c) >= 0) ? 1 : 0;
 197         }
 198     }
 199 }