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