001/* 002 * This file is part of OCaml-Java runtime. 003 * Copyright (C) 2007-2013 Xavier Clerc. 004 * 005 * OCaml-Java runtime is free software; you can redistribute it and/or modify 006 * it under the terms of the GNU Lesser General Public License as published by 007 * the Free Software Foundation; either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * OCaml-Java runtime is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.ocamljava.runtime.wrappers; 020 021import java.lang.reflect.Constructor; 022import java.lang.reflect.InvocationTargetException; 023import java.util.HashMap; 024import java.util.Map; 025 026import org.ocamljava.runtime.context.CodeState; 027import org.ocamljava.runtime.context.CurrentContext; 028import org.ocamljava.runtime.context.PredefinedExceptions; 029import org.ocamljava.runtime.kernel.AbstractNativeRunner; 030import org.ocamljava.runtime.kernel.FailException; import org.ocamljava.runtime.kernel.Fail; 031import org.ocamljava.runtime.kernel.Misc; 032import org.ocamljava.runtime.kernel.OCamlJavaThread; 033import org.ocamljava.runtime.values.Value; 034 035/** 036 * The {@code OCamlException} class is the parent class of all wrapped 037 * OCaml exceptions, also acting as a fallback cases for when the wrapped 038 * exception cannot be precisely mapped. 039 * 040 * @author <a href="mailto:xclerc@ocamljava.org">Xavier Clerc</a> 041 * @version 2.0 042 * @since 2.0 043 */ 044public class OCamlException extends RuntimeException { 045 046 /** Serialization UID. */ 047 static final long serialVersionUID = 304215162058080925L; 048 049 /** Identifier for {@code OCamlException}. */ 050 public static final Object SLOT = new Object(); 051 052 static { 053 // registration is done here rather than in exception classes 054 // themselves because the static blocks are executed iff a class 055 // is referenced from a program 056 try { 057 OCamlException.register("Not_found", OCamlNotFoundException.class); 058 OCamlException.register("Out_of_memory", OCamlOutOfMemoryException.class); 059 OCamlException.register("Stack_overflow", OCamlStackOverflowException.class); 060 OCamlException.register("End_of_file", OCamlEndOfFileException.class); 061 OCamlException.register("Division_by_zero", OCamlDivisionByZeroException.class); 062 OCamlException.register("Sys_blocked_io", OCamlSysBlockedIOException.class); 063 OCamlException.register("Failure", OCamlFailureException.class); 064 OCamlException.register("Invalid_argument", OCamlInvalidArgumentException.class); 065 OCamlException.register("Sys_error", OCamlSysErrorException.class); 066 OCamlException.register("Match_failure", OCamlMatchFailureException.class); 067 OCamlException.register("Assert_failure", OCamlAssertFailureException.class); 068 OCamlException.register("Undefined_recursive_module", OCamlUndefinedRecursiveModuleException.class); 069 } catch (final Throwable t) { 070 // nothing to do 071 } // end try/catch 072 } // end static block 073 074 /** 075 * Constructs a new instance from an exception. 076 * @param fe exception to build instance from - should not be {@code null} 077 */ 078 public OCamlException(final FailException fe) { 079 super(fe); 080 setStackTrace(fe.getStackTrace()); 081 } // end constructor(FailException) 082 083 /** 084 * Constructs a new instance from a value. <br/> 085 * Equivalent to {@code OCamlException(new FailException(v))} 086 * @param v value to build instance from - should not be {@code null} 087 */ 088 public OCamlException(final Value v) { 089 super(new FailException(v)); 090 } // end constructor(Value) 091 092 /** 093 * Returns the underlying exception. 094 * @return the underlying exception 095 */ 096 public final FailException getFailException() { 097 return (FailException) getCause(); 098 } // end method 'getFailException()' 099 100 /** 101 * Returns the name (that acts as an identifier) of the underlying exception. 102 * @return the name of the underlying exception 103 */ 104 public final String getOCamlName() { 105 final FailException e = (FailException) getCause(); 106 final Value v = e.getValue(); 107 return v.get0().get0().asString(); 108 } // end method 'getOCamlName()' 109 110 /** 111 * Returns the string representation of the underlying exception. 112 * @return the string representation of the underlying exception 113 */ 114 public final String getOCamlStringRepresentation() { 115 final FailException e = (FailException) getCause(); 116 final Value v = e.getValue(); 117 return Misc.convertException(v, null); 118 } // end method 'getOCamlStringRepresentation()' 119 120 /** 121 * {@inheritDoc} 122 */ 123 @Override 124 public final String toString() { 125 return getOCamlStringRepresentation(); 126 } // end method 'toString()' 127 128 /** 129 * Wraps a {@code FailException} exception into a {@code OCamlException} one, 130 * using the map of registered exceptions. 131 * @param fe exception to wrap - should not be {@code null} 132 * @return a new {@code OCamlException} instance wrapping the passed exception 133 */ 134 public static OCamlException wrap(final FailException fe) { 135 assert fe != null : "null fe"; 136 final Value v = fe.getValue(); 137 final String id = v.get0().get0().asString(); 138 final Constructor<?> cstr = getExceptionMap().get(id); 139 if (cstr != null) { 140 try { 141 return (OCamlException) cstr.newInstance(fe); 142 } catch (final InstantiationException | IllegalAccessException | InvocationTargetException e) { 143 return new OCamlException(fe); 144 } // end try/catch 145 } else { 146 return new OCamlException(fe); 147 } // end if/else 148 } // end method 'wrap(FailException)' 149 150 /** 151 * Registers an exception in the context. <br/> 152 * The class implementing the exception should provide a constructor 153 * with signature {@code (FailException)}. 154 * @param id exception identifier - should not be {@code null} 155 * @param cl class of exception to register - should not be {@code null} 156 */ 157 public static void register(final String id, final Class<?> cl) { 158 assert id != null : "null id"; 159 assert cl != null : "null cl"; 160 try { 161 final Map<String, Constructor<?>> map = OCamlException.getExceptionMap(); 162 map.put(id, cl.getConstructor(FailException.class)); 163 } catch (final NoSuchMethodException nsme) { 164 // nothing to do 165 } // end try/catch 166 } // end method 'register(String, Class<?>)' 167 168 /** 169 * Returns the tag for the predefined exception whose identifier is given. 170 * @param id predefined exception identifier - should not be {@code null} 171 * @return the tag for the predefined exception whose identifier is given, 172 * {@code null} if such an exception does not exit 173 */ 174 static Value getTag(final String id) { // XXX duplicated somewhere, should be in PredefinedExceptions 175 assert id != null : "null id"; 176 final PredefinedExceptions exceptions = CurrentContext.getPredefinedExceptions(); 177 switch (id) { 178 case "Out_of_memory": return exceptions.exnOutOfMemory; 179 case "Sys_error": return exceptions.exnSysError; 180 case "Failure": return exceptions.exnFailure; 181 case "Invalid_argument": return exceptions.exnInvalidArgument; 182 case "End_of_file": return exceptions.exnEndOfFile; 183 case "Division_by_zero": return exceptions.exnDivisionByZero; 184 case "Not_found": return exceptions.exnNotFound; 185 case "Match_failure": return exceptions.exnMatchFailure; 186 case "Stack_overflow": return exceptions.exnStackOverflow; 187 case "Sys_blocked_io": return exceptions.exnSysBlockedIO; 188 case "Assert_failure": return exceptions.exnAssertFailure; 189 case "Undefined_recursive_module": return exceptions.exnUndefinedRecursiveModule; 190 default: return null; 191 } // end switch 192 } // end method 'getTag(String)' 193 194 /** 195 * Returns the map of registered exceptions, as stored in the context. 196 * @return the map of registered exceptions 197 */ 198 @SuppressWarnings("unchecked") 199 private static synchronized Map<String, Constructor<?>> getExceptionMap() { 200 final CodeState cs = CurrentContext.getCodeState(); 201 final Map<String, Constructor<?>> res = 202 (Map<String, Constructor<?>>) cs.getSlot(OCamlException.SLOT); 203 if (res == null) { 204 final Map<String, Constructor<?>> slot = new HashMap<>(); 205 cs.registerSlot(OCamlException.SLOT, slot); 206 return slot; 207 } else { 208 return res; 209 } // end if/else 210 } // end method 'getExceptionMap()' 211 212} // end class 'OCamlException'