rt/emul/compact/src/main/java/java/lang/invoke/ProxyClassesDumper.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 09 Aug 2014 11:11:13 +0200
branchjdk8-b132
changeset 1646 c880a8a8803b
permissions -rw-r--r--
Batch of classes necessary to implement invoke dynamic interfaces. Taken from JDK8 build 132
     1 /*
     2  * Copyright (c) 2013, 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.invoke;
    26 
    27 import sun.util.logging.PlatformLogger;
    28 
    29 import java.io.FilePermission;
    30 import java.nio.file.Files;
    31 import java.nio.file.InvalidPathException;
    32 import java.nio.file.Path;
    33 import java.nio.file.Paths;
    34 import java.security.AccessController;
    35 import java.security.PrivilegedAction;
    36 import java.util.Objects;
    37 import java.util.concurrent.atomic.AtomicBoolean;
    38 
    39 /**
    40  * Helper class used by InnerClassLambdaMetafactory to log generated classes
    41  *
    42  * @implNote
    43  * <p> Because this class is called by LambdaMetafactory, make use
    44  * of lambda lead to recursive calls cause stack overflow.
    45  */
    46 final class ProxyClassesDumper {
    47     private static final char[] HEX = {
    48         '0', '1', '2', '3', '4', '5', '6', '7',
    49         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
    50     };
    51     private static final char[] BAD_CHARS = {
    52         '\\', ':', '*', '?', '"', '<', '>', '|'
    53     };
    54     private static final String[] REPLACEMENT = {
    55         "%5C", "%3A", "%2A", "%3F", "%22", "%3C", "%3E", "%7C"
    56     };
    57 
    58     private final Path dumpDir;
    59 
    60     public static ProxyClassesDumper getInstance(String path) {
    61         if (null == path) {
    62             return null;
    63         }
    64         try {
    65             path = path.trim();
    66             final Path dir = Paths.get(path.length() == 0 ? "." : path);
    67             AccessController.doPrivileged(new PrivilegedAction<Void>() {
    68                     @Override
    69                     public Void run() {
    70                         validateDumpDir(dir);
    71                         return null;
    72                     }
    73                 }, null, new FilePermission("<<ALL FILES>>", "read, write"));
    74             return new ProxyClassesDumper(dir);
    75         } catch (InvalidPathException ex) {
    76             PlatformLogger.getLogger(ProxyClassesDumper.class.getName())
    77                           .warning("Path " + path + " is not valid - dumping disabled", ex);
    78         } catch (IllegalArgumentException iae) {
    79             PlatformLogger.getLogger(ProxyClassesDumper.class.getName())
    80                           .warning(iae.getMessage() + " - dumping disabled");
    81         }
    82         return null;
    83     }
    84 
    85     private ProxyClassesDumper(Path path) {
    86         dumpDir = Objects.requireNonNull(path);
    87     }
    88 
    89     private static void validateDumpDir(Path path) {
    90         if (!Files.exists(path)) {
    91             throw new IllegalArgumentException("Directory " + path + " does not exist");
    92         } else if (!Files.isDirectory(path)) {
    93             throw new IllegalArgumentException("Path " + path + " is not a directory");
    94         } else if (!Files.isWritable(path)) {
    95             throw new IllegalArgumentException("Directory " + path + " is not writable");
    96         }
    97     }
    98 
    99     public static String encodeForFilename(String className) {
   100         final int len = className.length();
   101         StringBuilder sb = new StringBuilder(len);
   102 
   103         for (int i = 0; i < len; i++) {
   104             char c = className.charAt(i);
   105             // control characters
   106             if (c <= 31) {
   107                 sb.append('%');
   108                 sb.append(HEX[c >> 4 & 0x0F]);
   109                 sb.append(HEX[c & 0x0F]);
   110             } else {
   111                 int j = 0;
   112                 for (; j < BAD_CHARS.length; j++) {
   113                     if (c == BAD_CHARS[j]) {
   114                         sb.append(REPLACEMENT[j]);
   115                         break;
   116                     }
   117                 }
   118                 if (j >= BAD_CHARS.length) {
   119                     sb.append(c);
   120                 }
   121             }
   122         }
   123 
   124         return sb.toString();
   125     }
   126 
   127     public void dumpClass(String className, final byte[] classBytes) {
   128         Path file;
   129         try {
   130             file = dumpDir.resolve(encodeForFilename(className) + ".class");
   131         } catch (InvalidPathException ex) {
   132             PlatformLogger.getLogger(ProxyClassesDumper.class.getName())
   133                           .warning("Invalid path for class " + className);
   134             return;
   135         }
   136 
   137         try {
   138             Path dir = file.getParent();
   139             Files.createDirectories(dir);
   140             Files.write(file, classBytes);
   141         } catch (Exception ignore) {
   142             PlatformLogger.getLogger(ProxyClassesDumper.class.getName())
   143                           .warning("Exception writing to path at " + file.toString());
   144             // simply don't care if this operation failed
   145         }
   146     }
   147 }