< prev index next >

src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/TranslatedException.java

Print this page




   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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.vm.ci.hotspot;
  24 
  25 import java.lang.reflect.InvocationTargetException;
  26 import java.util.ArrayList;
  27 import java.util.Arrays;
  28 import java.util.Collections;
  29 import java.util.Formatter;
  30 import java.util.List;
  31 import java.util.Objects;
  32 
  33 /**
  34  * Support for translating exceptions between different runtime heaps.
  35  */
  36 @SuppressWarnings("serial")
  37 final class TranslatedException extends Exception {
  38 
  39     private TranslatedException(String message, Throwable translationFailure) {
  40         super("[" + translationFailure + "]" + Objects.toString(message, ""));




  41     }
  42 
  43     /**
  44      * No need to record an initial stack trace since it will be manually overwritten.
  45      */
  46     @SuppressWarnings("sync-override")
  47     @Override
  48     public Throwable fillInStackTrace() {
  49         return this;
  50     }
  51 
  52     /**
  53      * Prints a stack trace for {@code throwable} and returns {@code true}. Used to print stack
  54      * traces only when assertions are enabled.
  55      */
  56     private static boolean printStackTrace(Throwable throwable) {
  57         throwable.printStackTrace();
  58         return true;
  59     }
  60 
  61     private static Throwable initCause(Throwable throwable, Throwable cause) {
  62         if (cause != null) {
  63             try {
  64                 throwable.initCause(cause);
  65             } catch (IllegalStateException e) {
  66                 // Cause could not be set or overwritten.
  67                 assert printStackTrace(e);
  68             }
  69         }
  70         return throwable;
  71     }
  72 
  73     private static Throwable create(String className, String message, Throwable cause) {
  74         // Try create with reflection first.
  75         try {
  76             Class<?> cls = Class.forName(className);
  77             if (cause != null) {
  78                 // Handle known exception types whose cause must be set in the constructor
  79                 if (cls == InvocationTargetException.class) {
  80                     return new InvocationTargetException(cause, message);
  81                 }
  82                 if (cls == ExceptionInInitializerError.class) {
  83                     return new ExceptionInInitializerError(cause);
  84                 }
  85             }
  86             if (message == null) {
  87                 return initCause((Throwable) cls.getConstructor().newInstance(), cause);
  88             }
  89             cls.getDeclaredConstructor(String.class);
  90             return initCause((Throwable) cls.getConstructor(String.class).newInstance(message), cause);
  91         } catch (Throwable translationFailure) {
  92             if (className.equals(TranslatedException.class.getName())) {
  93                 // Chop the class name when boxing another TranslatedException
  94                 return initCause(new TranslatedException(message, translationFailure), cause);
  95             }
  96             return initCause(new TranslatedException(null, translationFailure), cause);
  97         }
  98     }
  99 
 100     /**
 101      * Encodes an exception message to distinguish a null message from an empty message.
 102      *
 103      * @return {@code value} with a space prepended iff {@code value != null}
 104      */
 105     private static String encodeMessage(String value) {
 106         return value != null ? ' ' + value : value;
 107     }
 108 
 109     private static String decodeMessage(String value) {
 110         if (value.length() == 0) {
 111             return null;
 112         }
 113         return value.substring(1);
 114     }
 115 
 116     private static String encodedString(String value) {
 117         return Objects.toString(value, "").replace('|', '_');
 118     }
 119 
 120     /**
 121      * Encodes {@code throwable} including its stack and causes as a string. The encoding format of
 122      * a single exception is:
 123      *
 124      * <pre>
 125      * <exception class name> '|' <exception message> '|' <stack size> '|' [<class> '|' <method> '|' <file> '|' <line> '|' ]*
 126      * </pre>
 127      *
 128      * Each exception is encoded before the exception it causes.
 129      */
 130     @VMEntryPoint
 131     static String encodeThrowable(Throwable throwable) throws Throwable {
 132         try {
 133             Formatter enc = new Formatter();
 134             List<Throwable> throwables = new ArrayList<>();
 135             for (Throwable current = throwable; current != null; current = current.getCause()) {
 136                 throwables.add(current);
 137             }
 138 
 139             // Encode from inner most cause outwards
 140             Collections.reverse(throwables);
 141 
 142             for (Throwable current : throwables) {
 143                 enc.format("%s|%s|", current.getClass().getName(), encodedString(encodeMessage(current.getMessage())));
 144                 StackTraceElement[] stackTrace = current.getStackTrace();
 145                 if (stackTrace == null) {
 146                     stackTrace = new StackTraceElement[0];
 147                 }
 148                 enc.format("%d|", stackTrace.length);
 149                 for (int i = 0; i < stackTrace.length; i++) {
 150                     StackTraceElement frame = stackTrace[i];
 151                     if (frame != null) {
 152                         enc.format("%s|%s|%s|%d|", frame.getClassName(), frame.getMethodName(),
 153                                         encodedString(frame.getFileName()), frame.getLineNumber());
 154                     }
 155                 }
 156             }

 157             return enc.toString();
 158         } catch (Throwable e) {
 159             assert printStackTrace(e);
 160             try {
 161                 return e.getClass().getName() + "|" + encodedString(e.getMessage()) + "|0|";
 162             } catch (Throwable e2) {
 163                 assert printStackTrace(e2);
 164                 return "java.lang.Throwable|too many errors during encoding|0|";
 165             }
 166         }
 167     }
 168 
 169     /**
 170      * Gets the stack of the current thread without the frames between this call and the one just
 171      * below the frame of the first method in {@link CompilerToVM}. The chopped frames are specific
 172      * to the implementation of {@link HotSpotJVMCIRuntime#decodeThrowable(String)}.
 173      */
 174     private static StackTraceElement[] getStackTraceSuffix() {
 175         StackTraceElement[] stack = new Exception().getStackTrace();
 176         for (int i = 0; i < stack.length; i++) {
 177             StackTraceElement e = stack[i];
 178             if (e.getClassName().equals(CompilerToVM.class.getName())) {
 179                 return Arrays.copyOfRange(stack, i, stack.length);
 180             }
 181         }
 182         // This should never happen but since we're in exception handling
 183         // code, just return a safe value instead raising a nested exception.
 184         return new StackTraceElement[0];
 185     }
 186 
 187     /**
 188      * Decodes {@code encodedThrowable} into a {@link TranslatedException}.
 189      *
 190      * @param encodedThrowable an encoded exception in the format specified by
 191      *            {@link #encodeThrowable}
 192      */
 193     @VMEntryPoint
 194     static Throwable decodeThrowable(String encodedThrowable) {
 195         try {
 196             int i = 0;
 197             String[] parts = encodedThrowable.split("\\|");
 198             Throwable cause = null;
 199             Throwable throwable = null;
 200             while (i != parts.length) {
 201                 String exceptionClassName = parts[i++];
 202                 String exceptionMessage = decodeMessage(parts[i++]);
 203                 throwable = create(exceptionClassName, exceptionMessage, cause);
 204                 int stackTraceDepth = Integer.parseInt(parts[i++]);
 205 
 206                 StackTraceElement[] suffix = getStackTraceSuffix();
 207                 StackTraceElement[] stackTrace = new StackTraceElement[stackTraceDepth + suffix.length];
 208                 for (int j = 0; j < stackTraceDepth; j++) {
 209                     String className = parts[i++];
 210                     String methodName = parts[i++];
 211                     String fileName = parts[i++];
 212                     int lineNumber = Integer.parseInt(parts[i++]);
 213                     if (fileName.isEmpty()) {
 214                         fileName = null;
 215                     }
 216                     stackTrace[j] = new StackTraceElement(className, methodName, fileName, lineNumber);
 217                 }
 218                 System.arraycopy(suffix, 0, stackTrace, stackTraceDepth, suffix.length);
 219                 throwable.setStackTrace(stackTrace);
 220                 cause = throwable;





 221             }
 222             return throwable;
 223         } catch (Throwable translationFailure) {
 224             assert printStackTrace(translationFailure);
 225             return new TranslatedException("Error decoding exception: " + encodedThrowable, translationFailure);
 226         }
 227     }
 228 }


   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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.vm.ci.hotspot;
  24 


  25 import java.util.Arrays;

  26 import java.util.Formatter;

  27 import java.util.Objects;
  28 
  29 /**
  30  * Support for translating exceptions between different runtime heaps.
  31  */
  32 @SuppressWarnings("serial")
  33 final class TranslatedException extends Exception {
  34 
  35     private TranslatedException(String message) {
  36         super(message);
  37     }
  38 
  39     private TranslatedException(String message, Throwable cause) {
  40         super(message, cause);
  41     }
  42 
  43     /**
  44      * No need to record an initial stack trace since it will be manually overwritten.
  45      */
  46     @SuppressWarnings("sync-override")
  47     @Override
  48     public Throwable fillInStackTrace() {
  49         return this;
  50     }
  51 
  52     private static Throwable create(String className, String message) {





















  53         // Try create with reflection first.
  54         try {
  55             Class<?> cls = Class.forName(className);









  56             if (message == null) {
  57                 return (Throwable) cls.getConstructor().newInstance();
  58             }
  59             cls.getDeclaredConstructor(String.class);
  60             return (Throwable) cls.getConstructor(String.class).newInstance(message);
  61         } catch (Throwable ignore) {





  62         }

  63 
  64         if (className.equals(TranslatedException.class.getName())) {
  65             // Chop the class name when boxing another TranslatedException
  66             return new TranslatedException(message);
  67         }




  68 
  69         if (message == null) {
  70             return new TranslatedException(className);

  71         }
  72         return new TranslatedException(className + ": " + message);
  73     }
  74 
  75     private static String encodedString(String value) {
  76         return Objects.toString(value, "").replace('|', '_');
  77     }
  78 
  79     /**
  80      * Encodes {@code throwable} including its stack and causes as a string. The encoding format of
  81      * a single exception with its cause is:
  82      *
  83      * <pre>
  84      * <exception class name> '|' <exception message> '|' <stack size> '|' [<class> '|' <method> '|' <file> '|' <line> '|' ]*
  85      * </pre>
  86      *
  87      * Each cause is appended after the exception is it the cause of.
  88      */
  89     @VMEntryPoint
  90     static String encodeThrowable(Throwable throwable) throws Throwable {
  91         try {
  92             Formatter enc = new Formatter();
  93             Throwable current = throwable;
  94             do {
  95                 enc.format("%s|%s|", current.getClass().getName(), encodedString(current.getMessage()));







  96                 StackTraceElement[] stackTrace = current.getStackTrace();
  97                 if (stackTrace == null) {
  98                     stackTrace = new StackTraceElement[0];
  99                 }
 100                 enc.format("%d|", stackTrace.length);
 101                 for (int i = 0; i < stackTrace.length; i++) {
 102                     StackTraceElement frame = stackTrace[i];
 103                     if (frame != null) {
 104                         enc.format("%s|%s|%s|%d|", frame.getClassName(), frame.getMethodName(),
 105                                         encodedString(frame.getFileName()), frame.getLineNumber());
 106                     }
 107                 }
 108                 current = current.getCause();
 109             } while (current != null);
 110             return enc.toString();
 111         } catch (Throwable e) {

 112             try {
 113                 return e.getClass().getName() + "|" + encodedString(e.getMessage()) + "|0|";
 114             } catch (Throwable e2) {

 115                 return "java.lang.Throwable|too many errors during encoding|0|";
 116             }
 117         }
 118     }
 119 
 120     /**
 121      * Gets the stack of the current thread without the frames between this call and the one just
 122      * below the frame of the first method in {@link CompilerToVM}. The chopped frames are specific
 123      * to the implementation of {@link HotSpotJVMCIRuntime#decodeThrowable(String)}.
 124      */
 125     private static StackTraceElement[] getStackTraceSuffix() {
 126         StackTraceElement[] stack = new Exception().getStackTrace();
 127         for (int i = 0; i < stack.length; i++) {
 128             StackTraceElement e = stack[i];
 129             if (e.getClassName().equals(CompilerToVM.class.getName())) {
 130                 return Arrays.copyOfRange(stack, i, stack.length);
 131             }
 132         }
 133         // This should never happen but since we're in exception handling
 134         // code, just return a safe value instead raising a nested exception.
 135         return new StackTraceElement[0];
 136     }
 137 
 138     /**
 139      * Decodes {@code encodedThrowable} into a {@link TranslatedException}.
 140      *
 141      * @param encodedThrowable an encoded exception in the format specified by
 142      *            {@link #encodeThrowable}
 143      */
 144     @VMEntryPoint
 145     static Throwable decodeThrowable(String encodedThrowable) {
 146         try {
 147             int i = 0;
 148             String[] parts = encodedThrowable.split("\\|");
 149             Throwable parent = null;
 150             Throwable result = null;
 151             while (i != parts.length) {
 152                 String exceptionClassName = parts[i++];
 153                 String exceptionMessage = parts[i++];
 154                 Throwable throwable = create(exceptionClassName, exceptionMessage);
 155                 int stackTraceDepth = Integer.parseInt(parts[i++]);
 156 
 157                 StackTraceElement[] suffix = getStackTraceSuffix();
 158                 StackTraceElement[] stackTrace = new StackTraceElement[stackTraceDepth + suffix.length];
 159                 for (int j = 0; j < stackTraceDepth; j++) {
 160                     String className = parts[i++];
 161                     String methodName = parts[i++];
 162                     String fileName = parts[i++];
 163                     int lineNumber = Integer.parseInt(parts[i++]);
 164                     if (fileName.isEmpty()) {
 165                         fileName = null;
 166                     }
 167                     stackTrace[j] = new StackTraceElement(className, methodName, fileName, lineNumber);
 168                 }
 169                 System.arraycopy(suffix, 0, stackTrace, stackTraceDepth, suffix.length);
 170                 throwable.setStackTrace(stackTrace);
 171                 if (parent != null) {
 172                     parent.initCause(throwable);
 173                 } else {
 174                     result = throwable;
 175                 }
 176                 parent = throwable;
 177             }
 178             return result;
 179         } catch (Throwable t) {
 180             return new TranslatedException("Error decoding exception: " + encodedThrowable, t);

 181         }
 182     }
 183 }
< prev index next >