github.com/goccy/go-jit@v0.0.0-20200514131505-ff78d45cf6af/internal/ccall/jit-except.c (about)

     1  /*
     2   * jit-except.c - Exception handling functions.
     3   *
     4   * Copyright (C) 2004  Southern Storm Software, Pty Ltd.
     5   *
     6   * This file is part of the libjit library.
     7   *
     8   * The libjit library is free software: you can redistribute it and/or
     9   * modify it under the terms of the GNU Lesser General Public License
    10   * as published by the Free Software Foundation, either version 2.1 of
    11   * the License, or (at your option) any later version.
    12   *
    13   * The libjit library is distributed in the hope that it will be useful,
    14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    16   * Lesser General Public License for more details.
    17   *
    18   * You should have received a copy of the GNU Lesser General Public
    19   * License along with the libjit library.  If not, see
    20   * <http://www.gnu.org/licenses/>.
    21   */
    22  
    23  #include "jit-internal.h"
    24  #include "jit-rules.h"
    25  #ifdef HAVE_STDLIB_H
    26  # include <stdlib.h>
    27  #endif
    28  #if defined(JIT_BACKEND_INTERP)
    29  # include "jit-interp.h"
    30  #endif
    31  #include <stdio.h>
    32  #include "jit-setjmp.h"
    33  
    34  /*@
    35  
    36  @cindex jit-except.h
    37  
    38  @*/
    39  
    40  /*@
    41   * @deftypefun {void *} jit_exception_get_last (void)
    42   * Get the last exception object that occurred on this thread, or NULL
    43   * if there is no exception object on this thread.  As far as @code{libjit}
    44   * is concerned, an exception is just a pointer.  The precise meaning of the
    45   * data at the pointer is determined by the front end.
    46   * @end deftypefun
    47  @*/
    48  void *jit_exception_get_last(void)
    49  {
    50  	jit_thread_control_t control = _jit_thread_get_control();
    51  	if(control)
    52  	{
    53  		return control->last_exception;
    54  	}
    55  	else
    56  	{
    57  		return 0;
    58  	}
    59  }
    60  
    61  /*@
    62   * @deftypefun {void *} jit_exception_get_last_and_clear (void)
    63   * Get the last exception object that occurred on this thread and also
    64   * clear the exception state to NULL.  This combines the effect of
    65   * both @code{jit_exception_get_last} and @code{jit_exception_clear_last}.
    66   * @end deftypefun
    67  @*/
    68  void *jit_exception_get_last_and_clear(void)
    69  {
    70  	jit_thread_control_t control = _jit_thread_get_control();
    71  	if(control)
    72  	{
    73  		void *obj = control->last_exception;
    74  		control->last_exception = 0;
    75  		return obj;
    76  	}
    77  	else
    78  	{
    79  		return 0;
    80  	}
    81  }
    82  
    83  /*@
    84   * @deftypefun void jit_exception_set_last (void *@var{object})
    85   * Set the last exception object that occurred on this thread, so that
    86   * it can be retrieved by a later call to @code{jit_exception_get_last}.
    87   * This is normally used by @code{jit_function_apply} to save the
    88   * exception object before returning to regular code.
    89   * @end deftypefun
    90  @*/
    91  void jit_exception_set_last(void *object)
    92  {
    93  	jit_thread_control_t control = _jit_thread_get_control();
    94  	if(control)
    95  	{
    96  		control->last_exception = object;
    97  	}
    98  }
    99  
   100  /*@
   101   * @deftypefun void jit_exception_clear_last (void)
   102   * Clear the last exception object that occurred on this thread.
   103   * This is equivalent to calling @code{jit_exception_set_last}
   104   * with a parameter of NULL.
   105   * @end deftypefun
   106  @*/
   107  void jit_exception_clear_last(void)
   108  {
   109  	jit_exception_set_last(0);
   110  }
   111  
   112  /*@
   113   * @deftypefun void jit_exception_throw (void *@var{object})
   114   * Throw an exception object within the current thread.  As far as
   115   * @code{libjit} is concerned, the exception object is just a pointer.
   116   * The precise meaning of the data at the pointer is determined
   117   * by the front end.
   118   *
   119   * Note: as an exception object works its way back up the stack,
   120   * it may be temporarily stored in memory that is not normally visible
   121   * to a garbage collector.  The front-end is responsible for taking steps
   122   * to "pin" the object so that it is uncollectable until explicitly
   123   * copied back into a location that is visible to the collector once more.
   124   * @end deftypefun
   125  @*/
   126  void jit_exception_throw(void *object)
   127  {
   128  	jit_thread_control_t control = _jit_thread_get_control();
   129  	if(control)
   130  	{
   131  		control->last_exception = object;
   132  		if(control->setjmp_head)
   133  		{
   134  			control->backtrace_head = control->setjmp_head->trace;
   135  			longjmp(control->setjmp_head->buf, 1);
   136  		}
   137  	}
   138  }
   139  
   140  /*@
   141   * @deftypefun void jit_exception_builtin (int @var{exception_type})
   142   * This function is called to report a builtin exception.
   143   * The JIT will automatically embed calls to this function wherever a
   144   * builtin exception needs to be reported.
   145   *
   146   * When a builtin exception occurs, the current thread's exception
   147   * handler is called to construct an appropriate object, which is
   148   * then thrown.
   149   *
   150   * If there is no exception handler set, or the handler returns NULL,
   151   * then @code{libjit} will print an error message to stderr and cause
   152   * the program to exit with a status of 1.  You normally don't want
   153   * this behavior and you should override it if possible.
   154   *
   155   * The following builtin exception types are currently supported:
   156   *
   157   * @table @code
   158   * @vindex JIT_RESULT_OK
   159   * @item JIT_RESULT_OK
   160   * The operation was performed successfully (value is 1).
   161   *
   162   * @vindex JIT_RESULT_OVERFLOW
   163   * @item JIT_RESULT_OVERFLOW
   164   * The operation resulted in an overflow exception (value is 0).
   165   *
   166   * @vindex JIT_RESULT_ARITHMETIC
   167   * @item JIT_RESULT_ARITHMETIC
   168   * The operation resulted in an arithmetic exception.  i.e. an attempt was
   169   * made to divide the minimum integer value by -1 (value is -1).
   170   *
   171   * @vindex JIT_RESULT_DIVISION_BY_ZERO
   172   * @item JIT_RESULT_DIVISION_BY_ZERO
   173   * The operation resulted in a division by zero exception (value is -2).
   174   *
   175   * @vindex JIT_RESULT_COMPILE_ERROR
   176   * @item JIT_RESULT_COMPILE_ERROR
   177   * An error occurred when attempting to dynamically compile a function
   178   * (value is -3).
   179   *
   180   * @vindex JIT_RESULT_OUT_OF_MEMORY
   181   * @item JIT_RESULT_OUT_OF_MEMORY
   182   * The system ran out of memory while performing an operation (value is -4).
   183   *
   184   * @vindex JIT_RESULT_NULL_REFERENCE
   185   * @item JIT_RESULT_NULL_REFERENCE
   186   * An attempt was made to dereference a NULL pointer (value is -5).
   187   *
   188   * @vindex JIT_RESULT_NULL_FUNCTION
   189   * @item JIT_RESULT_NULL_FUNCTION
   190   * An attempt was made to call a function with a NULL function pointer
   191   * (value is -6).
   192   *
   193   * @vindex JIT_RESULT_CALLED_NESTED
   194   * @item JIT_RESULT_CALLED_NESTED
   195   * An attempt was made to call a nested function from a non-nested context
   196   * (value is -7).
   197   *
   198   * @vindex JIT_RESULT_OUT_OF_BOUNDS
   199   * @item JIT_RESULT_OUT_OF_BOUNDS
   200   * The operation resulted in an out of bounds array access (value is -8).
   201   *
   202   * @vindex JIT_RESULT_UNDEFINED_LABEL
   203   * @item JIT_RESULT_UNDEFINED_LABEL
   204   * A branch operation used a label that was not defined anywhere in the
   205   * function (value is -9).
   206   * @end table
   207   * @end deftypefun
   208  @*/
   209  void jit_exception_builtin(int exception_type)
   210  {
   211  	jit_exception_func handler;
   212  	void *object;
   213  	static const char * const messages[11] = {
   214  		"Success",
   215  		"Overflow during checked arithmetic operation",
   216  		"Arithmetic exception (dividing the minimum integer by -1)",
   217  		"Division by zero",
   218  		"Error during function compilation",
   219  		"Out of memory",
   220  		"Null pointer dereferenced",
   221  		"Null function pointer called",
   222  		"Nested function called from non-nested context",
   223  		"Array index out of bounds",
   224  		"Undefined label"
   225  	};
   226  	#define	num_messages	(sizeof(messages) / sizeof(const char *))
   227  
   228  	/* Get the exception handler for this thread */
   229  	handler = jit_exception_get_handler();
   230  
   231  	/* Invoke the exception handler to create an appropriate object */
   232  	if(handler)
   233  	{
   234  		object = (*handler)(exception_type);
   235  		if(object)
   236  		{
   237  			jit_exception_throw(object);
   238  		}
   239  	}
   240  
   241  	/* We don't have an exception handler, so print a message and exit */
   242  	fputs("A builtin JIT exception could not be handled:\n", stderr);
   243  	exception_type = -(exception_type - 1);
   244  	if(exception_type >= 0 && exception_type < (int)num_messages)
   245  	{
   246  		fputs(messages[exception_type], stderr);
   247  	}
   248  	else
   249  	{
   250  		fprintf(stderr, "Unknown builtin exception %d",
   251  				(-exception_type) + 1);
   252  	}
   253  	putc('\n', stderr);
   254  	exit(1);
   255  }
   256  
   257  /*@
   258   * @deftypefun jit_exception_func jit_exception_set_handler (jit_exception_func @var{handler})
   259   * Set the builtin exception handler for the current thread.
   260   * Returns the previous exception handler.
   261   * @end deftypefun
   262  @*/
   263  jit_exception_func jit_exception_set_handler
   264  	(jit_exception_func handler)
   265  {
   266  	jit_exception_func previous;
   267  	jit_thread_control_t control = _jit_thread_get_control();
   268  	if(control)
   269  	{
   270  		previous = control->exception_handler;
   271  		control->exception_handler = handler;
   272  		return previous;
   273  	}
   274  	else
   275  	{
   276  		return 0;
   277  	}
   278  }
   279  
   280  /*@
   281   * @deftypefun jit_exception_func jit_exception_get_handler (void)
   282   * Get the builtin exception handler for the current thread.
   283   * @end deftypefun
   284  @*/
   285  jit_exception_func jit_exception_get_handler(void)
   286  {
   287  	jit_thread_control_t control = _jit_thread_get_control();
   288  	if(control)
   289  	{
   290  		return control->exception_handler;
   291  	}
   292  	else
   293  	{
   294  		return 0;
   295  	}
   296  }
   297  
   298  /*
   299   * Structure of a stack trace.
   300   */
   301  struct jit_stack_trace
   302  {
   303  	unsigned int		size;
   304  	void			   *items[1];
   305  };
   306  
   307  /*@
   308   * @deftypefun jit_stack_trace_t jit_exception_get_stack_trace (void)
   309   * Create an object that represents the current call stack.
   310   * This is normally used to indicate the location of an exception.
   311   * Returns NULL if a stack trace is not available, or there is
   312   * insufficient memory to create it.
   313   * @end deftypefun
   314  @*/
   315  jit_stack_trace_t jit_exception_get_stack_trace(void)
   316  {
   317  	jit_stack_trace_t trace;
   318  	unsigned int size;
   319  	jit_unwind_context_t unwind;
   320  
   321  	/* Count the number of items in the current thread's call stack */
   322  	size = 0;
   323  	if(jit_unwind_init(&unwind, NULL))
   324  	{
   325  		do
   326  		{
   327  			size++;
   328  		}
   329  		while(jit_unwind_next_pc(&unwind));
   330  		jit_unwind_free(&unwind);
   331  	}
   332  
   333  	/* Bail out if the stack is not available */
   334  	if(size == 0)
   335  	{
   336  		return 0;
   337  	}
   338  
   339  	/* Allocate memory for the stack trace */
   340  	trace = (jit_stack_trace_t) jit_malloc(sizeof(struct jit_stack_trace)
   341  					       + size * sizeof(void *)
   342  					       - sizeof(void *));
   343  	if(!trace)
   344  	{
   345  		return 0;
   346  	}
   347  	trace->size = size;
   348  
   349  	/* Populate the stack trace with the items we counted earlier */
   350  	size = 0;
   351  	if(jit_unwind_init(&unwind, NULL))
   352  	{
   353  		do
   354  		{
   355  			trace->items[size] = jit_unwind_get_pc(&unwind);
   356  			size++;
   357  		}
   358  		while(jit_unwind_next_pc(&unwind));
   359  		jit_unwind_free(&unwind);
   360  	}
   361  	else
   362  	{
   363  		jit_free(trace);
   364  		return 0;
   365  	}
   366  
   367  	return trace;
   368  }
   369  
   370  /*@
   371   * @deftypefun {unsigned int} jit_stack_trace_get_size (jit_stack_trace_t @var{trace})
   372   * Get the size of a stack trace.
   373   * @end deftypefun
   374  @*/
   375  unsigned int jit_stack_trace_get_size(jit_stack_trace_t trace)
   376  {
   377  	if(trace)
   378  	{
   379  		return trace->size;
   380  	}
   381  	else
   382  	{
   383  		return 0;
   384  	}
   385  }
   386  
   387  /*@
   388   * @deftypefun jit_function_t jit_stack_trace_get_function (jit_context_t @var{context}, jit_stack_trace_t @var{trace}, unsigned int @var{posn})
   389   * Get the function that is at position @var{posn} within a stack trace.
   390   * Position 0 is the function that created the stack trace.  If this
   391   * returns NULL, then it indicates that there is a native callout at
   392   * @var{posn} within the stack trace.
   393   * @end deftypefun
   394  @*/
   395  jit_function_t
   396  jit_stack_trace_get_function(jit_context_t context, jit_stack_trace_t trace, unsigned int posn)
   397  {
   398  	if(trace && posn < trace->size)
   399  	{
   400  		void *func_info = _jit_memory_find_function_info(context, trace->items[posn]);
   401  		if(func_info)
   402  		{
   403  			return _jit_memory_get_function(context, func_info);
   404  		}
   405  	}
   406  	return 0;
   407  }
   408  
   409  /*@
   410   * @deftypefun {void *} jit_stack_trace_get_pc (jit_stack_trace_t @var{trace}, unsigned int @var{posn})
   411   * Get the program counter that corresponds to position @var{posn}
   412   * within a stack trace.  This is the point within the function
   413   * where execution had reached at the time of the trace.
   414   * @end deftypefun
   415  @*/
   416  void *jit_stack_trace_get_pc
   417  	(jit_stack_trace_t trace, unsigned int posn)
   418  {
   419  	if(trace && posn < trace->size)
   420  	{
   421  		return trace->items[posn];
   422  	}
   423  	else
   424  	{
   425  		return 0;
   426  	}
   427  }
   428  
   429  /*@
   430   * @deftypefun {unsigned int} jit_stack_trace_get_offset (jit_stack_trace_t @var{trace}, unsigned int @var{posn})
   431   * Get the bytecode offset that is recorded for position @var{posn}
   432   * within a stack trace.  This will be @code{JIT_NO_OFFSET} if there
   433   * is no bytecode offset associated with @var{posn}.
   434   * @end deftypefun
   435  @*/
   436  unsigned int
   437  jit_stack_trace_get_offset(jit_context_t context, jit_stack_trace_t trace, unsigned int posn)
   438  {
   439  	void *func_info;
   440  	jit_function_t func;
   441  
   442  	if(!trace || posn >= trace->size)
   443  	{
   444  		return JIT_NO_OFFSET;
   445  	}
   446  
   447  	func_info = _jit_memory_find_function_info(context, trace->items[posn]);
   448  	if(!func_info)
   449  	{
   450  		return JIT_NO_OFFSET;
   451  	}
   452  	func = _jit_memory_get_function(context, func_info);
   453  	if(!func)
   454  	{
   455  		return JIT_NO_OFFSET;
   456  	}
   457  
   458  	return _jit_function_get_bytecode(func, func_info, trace->items[posn], 0);
   459  }
   460  
   461  /*@
   462   * @deftypefun void jit_stack_trace_free (jit_stack_trace_t @var{trace})
   463   * Free the memory associated with a stack trace.
   464   * @end deftypefun
   465  @*/
   466  void jit_stack_trace_free(jit_stack_trace_t trace)
   467  {
   468  	if(trace)
   469  	{
   470  		jit_free(trace);
   471  	}
   472  }
   473  
   474  void _jit_backtrace_push(jit_backtrace_t trace, void *pc)
   475  {
   476  	jit_thread_control_t control = _jit_thread_get_control();
   477  	if(control)
   478  	{
   479  		trace->parent = control->backtrace_head;
   480  		trace->pc = pc;
   481  		trace->security_object = 0;
   482  		trace->free_security_object = 0;
   483  		control->backtrace_head = trace;
   484  	}
   485  	else
   486  	{
   487  		trace->parent = 0;
   488  		trace->pc = pc;
   489  		trace->security_object = 0;
   490  		trace->free_security_object = 0;
   491  	}
   492  }
   493  
   494  void _jit_backtrace_pop(void)
   495  {
   496  	jit_thread_control_t control = _jit_thread_get_control();
   497  	jit_backtrace_t trace;
   498  	if(control)
   499  	{
   500  		trace = control->backtrace_head;
   501  		if(trace)
   502  		{
   503  			control->backtrace_head = trace->parent;
   504  			if(trace->security_object && trace->free_security_object)
   505  			{
   506  				(*(trace->free_security_object))(trace->security_object);
   507  			}
   508  		}
   509  	}
   510  }
   511  
   512  void _jit_backtrace_set(jit_backtrace_t trace)
   513  {
   514  	jit_thread_control_t control = _jit_thread_get_control();
   515  	if(control)
   516  	{
   517  		control->backtrace_head = trace;
   518  	}
   519  }
   520  
   521  void _jit_unwind_push_setjmp(jit_jmp_buf *jbuf)
   522  {
   523  	jit_thread_control_t control = _jit_thread_get_control();
   524  	if(control)
   525  	{
   526  		jbuf->trace = control->backtrace_head;
   527  		jbuf->catch_pc = 0;
   528  		jbuf->parent = control->setjmp_head;
   529  		control->setjmp_head = jbuf;
   530  	}
   531  }
   532  
   533  void _jit_unwind_pop_setjmp(void)
   534  {
   535  	jit_thread_control_t control = _jit_thread_get_control();
   536  	if(control && control->setjmp_head)
   537  	{
   538  		control->backtrace_head = control->setjmp_head->trace;
   539  		control->setjmp_head = control->setjmp_head->parent;
   540  	}
   541  }
   542  
   543  void _jit_unwind_pop_and_rethrow(void)
   544  {
   545  	_jit_unwind_pop_setjmp();
   546  	jit_exception_throw(jit_exception_get_last());
   547  }