Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 9 Jan 1999 12:05:45 +0900 (JST)
From:      dcs@newsguy.com
To:        FreeBSD-gnats-submit@FreeBSD.ORG
Subject:   kern/9402: Loader does not support exception handling
Message-ID:  <199901090305.MAA00567@local.ocn.ne.jp>

next in thread | raw e-mail | index | archive | help

>Number:         9402
>Category:       kern
>Synopsis:       Loader's builtin forth has no support for exception handling
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Jan  8 19:10:01 PST 1999
>Closed-Date:
>Last-Modified:
>Originator:     Daniel C. Sobral
>Release:        FreeBSD 3.0-CURRENT i386
>Organization:
>Environment:

	Three stage boot's loader.

>Description:

	Currently, loader has no set of words to deal with exception
handling. This adds extra complexity to the operation of builtin words,
if they are ever to return error codes (and may, in fact, preclude it).

>How-To-Repeat:

	UTSL?

>Fix:

	I propose ANS Forth standard's exception handling wordset. It
is composed of the words CATCH and THROW. CATCH takes an xt, saves
context and then acts like EXECUTE. There are two possible exits for
CATCH. First, if, during xt's execution, a THROW is executed with a
non-zero value passed, catch restores the context (this being at
defined as, at least, the two stack pointers, though the fix below
saves the entire FICL_VM structure, plus the FICL_STACK structures
pointed by FICL_VM elements) and pushes the value passed to THROW on
the stack. The stack contains precisely the number of cells as when
CATCH was first executed, in the case. Second, if no THROW is
executed, or all executed THROW's are passed 0, catch just push a 0
on the stack. In this case, CATCH acts just like "EXECUTE 0".

	Catch must be (and is) nestable.

	Pending the commit of this fix, further patches to actually
make use of this feature on the loader will be submitted.

	Apply the following fix to sys/boot/ficl/words.c:


--- sys/boot/ficl/words.c.orig	Sat Jan  9 05:23:59 1999
+++ sys/boot/ficl/words.c	Sat Jan  9 11:50:32 1999
@@ -4046,6 +4046,150 @@
     return;
 }
 
+/***************** freebsd added exception handling words *******************/
+
+/*
+ * Catch, from ANS Forth standard. Installs a safety net, then EXECUTE
+ * the word in ToS. If an exception happens, restore the state to what
+ * it was before, and pushes the exception value on the stack. If not,
+ * push zero.
+ *
+ * Notice that Catch implements an inner interpreter. This is ugly,
+ * but given how ficl works, it cannot be helped. The problem is that
+ * colon definitions will be executed *after* the function returns,
+ * while "code" definitions will be executed immediately. I considered
+ * other solutions to this problem, but all of them shared the same
+ * basic problem (with added disadvantages): if ficl ever changes it's
+ * inner thread modus operandi, one would have to fix this word.
+ *
+ * More comments can be found throughout catch's code.
+ *
+ * Daniel C. Sobral	Jan 09/1999
+ */
+
+static void catch(FICL_VM *pVM)
+{
+	int		except;
+	jmp_buf		vmState;
+	FICL_VM		VM;
+	FICL_STACK	pStack;
+	FICL_STACK	rStack;
+	FICL_WORD	*pFW;
+	IPTYPE		exitIP;
+
+	/*
+         * Get xt.
+	 * We need this *before* we save the stack pointer, or
+         * we'll have to pop one element out of the stack after
+         * an exception. I prefer to get done with it up front. :-)
+         */
+#if FICL_ROBUST > 1
+	vmCheckStack(pVM, 1, 0);
+#endif
+	pFW = stackPopPtr(pVM->pStack);
+
+	/* 
+	 * Save vm's state -- a catch will not back out environmental
+         * changes.
+	 *
+	 * We are *not* saving dictionary state, since it is
+	 * global instead of per vm, and we are not saving
+	 * stack contents, since we are not required to (and,
+	 * thus, it would be useless). We save pVM, and pVM
+	 * "stacks" (a structure containing general information
+	 * about it, including the current stack pointer).
+         */
+	memcpy((void*)&VM, (void*)pVM, sizeof(FICL_VM));
+	memcpy((void*)&pStack, (void*)pVM->pStack, sizeof(FICL_STACK));
+	memcpy((void*)&rStack, (void*)pVM->rStack, sizeof(FICL_STACK));
+
+	/*
+	 * Give pVM a jmp_buf
+	 */
+	pVM->pState = &vmState;
+
+	/*
+	 * Safety net
+	 */
+	except = setjmp(vmState);
+
+	/*
+	 * And now, choose what to do depending on except.
+	 */
+
+		/* Things having gone wrong... */
+	if(except) {
+		/* Restore vm's state */
+		memcpy((void*)pVM, (void*)&VM, sizeof(FICL_VM));
+		memcpy((void*)pVM->pStack, (void*)&pStack, sizeof(FICL_STACK));
+		memcpy((void*)pVM->rStack, (void*)&rStack, sizeof(FICL_STACK));
+
+		/* Push error */
+		stackPushINT32(pVM->pStack, except);
+
+		/* Things being ok... */
+	} else {
+		/*
+		 * We need to know when to exit the inner loop
+		 * Colonp, the "code" for colon words, just pushes
+		 * the word's IP onto the RP, and expect the inner
+		 * interpreter to do the rest. Well, I'd rather have
+		 * it done *before* I return from this function,
+		 * losing the automatic variables I'm using to save
+		 * state. Sure, I could save this on dynamic memory
+		 * and save state on RP, or I could even implement
+		 * the poor man's version of this word in Forth with
+		 * sp@, sp!, rp@ and rp!, but we have a lot of state
+		 * neatly tucked away in pVM, so why not save it?
+		 */
+		exitIP = pVM->ip;
+
+		/* Execute the xt -- inline code for vmExecute */
+
+		pVM->runningWord = pFW;
+		pFW->code(pVM);
+
+		/*
+		 * Run the inner loop until we get back to exitIP
+		 */
+		for (; pVM->ip != exitIP;) {
+			pFW = *pVM->ip++;
+
+			/* Inline code for vmExecute */
+			pVM->runningWord = pFW;
+			pFW->code(pVM);
+		}
+
+
+		/* Restore just the setjmp vector */
+		pVM->pState = VM.pState;
+
+		/* Push 0 -- everything is ok */
+		stackPushINT32(pVM->pStack, 0);
+	}
+}
+
+/*
+ * Throw -- maybe vmThow already do what's required, but I don't really
+ * know what happens when you longjmp(buf, 0). From ANS Forth standard.
+ *
+ * Anyway, throw takes the ToS and, if that's different from zero,
+ * returns to the last executed catch context. Further throws will
+ * unstack previously executed "catches", in LIFO mode.
+ *
+ * Daniel C. Sobral	Jan 09/1999
+ */
+
+static void throw(FICL_VM *pVM)
+{
+	int except;
+	
+	except = stackPopINT32(pVM->pStack);
+
+	if (except)
+		vmThrow(pVM, except);
+}
+
 /************************* freebsd added I/O words **************************/
 
 /*          fopen - open a file and return new fd on stack.
@@ -4382,6 +4526,8 @@
     dictAppendWord(dp, "key?",	    keyQuestion,    FW_DEFAULT);
     dictAppendWord(dp, "ms",        ms,             FW_DEFAULT);
     dictAppendWord(dp, "seconds",   pseconds,       FW_DEFAULT);
+    dictAppendWord(dp, "catch",     catch,          FW_DEFAULT);
+    dictAppendWord(dp, "throw",     throw,          FW_DEFAULT);
 
     /*
     ** Set CORE environment query values

>Release-Note:
>Audit-Trail:
>Unformatted:

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199901090305.MAA00567>