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.kernel.AbstractNativeRunner; 029import org.ocamljava.runtime.kernel.Fail; 030import org.ocamljava.runtime.kernel.Misc; 031import org.ocamljava.runtime.kernel.OCamlJavaThread; 032import org.ocamljava.runtime.values.Value; 033 034/** 035 * The {@code OCamlException} class is the parent class of all wrapped 036 * OCaml exceptions, also acting as a fallback cases for when the wrapped 037 * exception cannot be precisely mapped. 038 * 039 * @author <a href="mailto:xclerc@ocamljava.org">Xavier Clerc</a> 040 * @version 2.0 041 * @since 2.0 042 */ 043public class OCamlException extends RuntimeException { 044 045 /** Serialization UID. */ 046 static final long serialVersionUID = 304215162058080925L; 047 048 /** Identifier for {@code OCamlException}. */ 049 public static final Object SLOT = new Object(); 050 051 static { 052 // registration is done here rather than in exception classes 053 // themselves because the static blocks are executed iff a class 054 // is referenced from a program 055 try { 056 OCamlException.register("Not_found", OCamlNotFoundException.class); 057 OCamlException.register("Out_of_memory", OCamlOutOfMemoryException.class); 058 OCamlException.register("Stack_overflow", OCamlStackOverflowException.class); 059 OCamlException.register("End_of_file", OCamlEndOfFileException.class); 060 OCamlException.register("Division_by_zero", OCamlDivisionByZeroException.class); 061 OCamlException.register("Sys_blocked_io", OCamlSysBlockedIOException.class); 062 OCamlException.register("Failure", OCamlFailureException.class); 063 OCamlException.register("Invalid_argument", OCamlInvalidArgumentException.class); 064 OCamlException.register("Sys_error", OCamlSysErrorException.class); 065 OCamlException.register("Match_failure", OCamlMatchFailureException.class); 066 OCamlException.register("Assert_failure", OCamlAssertFailureException.class); 067 OCamlException.register("Undefined_recursive_module", OCamlUndefinedRecursiveModuleException.class); 068 } catch (final Throwable t) { 069 // nothing to do 070 } // end try/catch 071 } // end static block 072 073 /** 074 * Constructs a new instance from an exception. 075 * @param fe exception to build instance from - should not be {@code null} 076 */ 077 public OCamlException(final Fail.Exception fe) { 078 super(fe); 079 setStackTrace(fe.getStackTrace()); 080 } // end constructor(Fail.Exception) 081 082 /** 083 * Constructs a new instance from a value. <br/> 084 * Equivalent to {@code OCamlException(new Fail.Exception(v))} 085 * @param v value to build instance from - should not be {@code null} 086 */ 087 public OCamlException(final Value v) { 088 super(new Fail.Exception(v)); 089 } // end constructor(Value) 090 091 /** 092 * Returns the underlying exception. 093 * @return the underlying exception 094 */ 095 public final Fail.Exception getFailException() { 096 return (Fail.Exception) getCause(); 097 } // end method 'getFailException()' 098 099 /** 100 * Returns the name (that acts as an identifier) of the underlying exception. 101 * @return the name of the underlying exception 102 */ 103 public final String getOCamlName() { 104 final Fail.Exception e = (Fail.Exception) getCause(); 105 final Value v = e.asValue(); 106 return v.get0().get0().asString(); 107 } // end method 'getOCamlName()' 108 109 /** 110 * Returns the string representation of the underlying exception. 111 * @return the string representation of the underlying exception 112 */ 113 public final String getOCamlStringRepresentation() { 114 final Fail.Exception e = (Fail.Exception) getCause(); 115 final Value v = e.asValue(); 116 return Misc.convertException(v, null); 117 } // end method 'getOCamlStringRepresentation()' 118 119 /** 120 * {@inheritDoc} 121 */ 122 @Override 123 public final String toString() { 124 return getOCamlStringRepresentation(); 125 } // end method 'toString()' 126 127 /** 128 * Wraps a {@code Fail.Exception} exception into a {@code OCamlException} one, 129 * using the map of registered exceptions. 130 * @param fe exception to wrap - should not be {@code null} 131 * @return a new {@code OCamlException} instance wrapping the passed exception 132 */ 133 public static OCamlException wrap(final Fail.Exception fe) { 134 assert fe != null : "null fe"; 135 final Value v = fe.asValue(); 136 final String id = v.get0().get0().asString(); 137 final Constructor<?> cstr = getExceptionMap().get(id); 138 if (cstr != null) { 139 try { 140 return (OCamlException) cstr.newInstance(fe); 141 } catch (final InstantiationException | IllegalAccessException | InvocationTargetException e) { 142 return new OCamlException(fe); 143 } // end try/catch 144 } else { 145 return new OCamlException(fe); 146 } // end if/else 147 } // end method 'wrap(Fail.Exception)' 148 149 /** 150 * Registers an exception in the context. <br/> 151 * The class implementing the exception should provide a constructor 152 * with signature {@code (Fail.Exception)}. 153 * @param id exception identifier - should not be {@code null} 154 * @param cl class of exception to register - should not be {@code null} 155 */ 156 public static void register(final String id, final Class<?> cl) { 157 assert id != null : "null id"; 158 assert cl != null : "null cl"; 159 try { 160 final Map<String, Constructor<?>> map = OCamlException.getExceptionMap(); 161 map.put(id, cl.getConstructor(Fail.Exception.class)); 162 } catch (final NoSuchMethodException nsme) { 163 // nothing to do 164 } // end try/catch 165 } // end method 'register(String, Class<?>)' 166 167 /** 168 * Returns the tag for the predefined exception whose identifier is given. 169 * @param id predefined exception identifier - should not be {@code null} 170 * @return the tag for the predefined exception whose identifier is given, 171 * {@code null} if such an exception does not exit 172 */ 173 static Value getTag(final String id) { 174 assert id != null : "null id"; 175 final AbstractNativeRunner runner = (AbstractNativeRunner) 176 OCamlJavaThread.getCodeRunner(); 177 switch (id) { 178 case "Out_of_memory": return runner.exnOutOfMemory; 179 case "Sys_error": return runner.exnSysError; 180 case "Failure": return runner.exnFailure; 181 case "Invalid_argument": return runner.exnInvalidArgument; 182 case "End_of_file": return runner.exnEndOfFile; 183 case "Division_by_zero": return runner.exnDivisionByZero; 184 case "Not_found": return runner.exnNotFound; 185 case "Match_failure": return runner.exnMatchFailure; 186 case "Stack_overflow": return runner.exnStackOverflow; 187 case "Sys_blocked_io": return runner.exnSysBlockedIO; 188 case "Assert_failure": return runner.exnAssertFailure; 189 case "Undefined_recursive_module": return runner.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'