Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 10 Jan 1999 12:00:12 +0900 (JST)
From:      dcs@newsguy.com
To:        FreeBSD-gnats-submit@FreeBSD.ORG
Subject:   kern/9412: Massive rewrite of loader's builtin error handling
Message-ID:  <199901100300.MAA00872@daniel.sobral>

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

>Number:         9412
>Category:       kern
>Synopsis:       Change the way loader passes error to ficl programs
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sat Jan  9 19:10:00 PST 1999
>Closed-Date:
>Last-Modified:
>Originator:     Daniel C. Sobral
>Release:        FreeBSD 3.0-CURRENT i386
>Organization:
>Environment:

	Three stage boot loader, before my own previous patch to FICL
adding Catch and Throw (what can I say... I forgot to check in that
revision...). Granted, that pr has not been even analized yet, and I'll
send a follow up to that.

>Description:

	Right now, all builtin words process errors *before* returning
to ficl. They do return a "ok" flag, but they are not very specific on
what seems to be the problem, and they print error messages before any
code has a chance to intervene.

	Enter ANS Forth EXCEPTION word set. We add this wordset to ficl
(backed by setjmp/longjmp), then change the builtin words to make use of
it to report errors. Error messages then get printed in bf_run, if
nobody intercepts them.

	As a bonus, bf_run becomes aware of others errors (not caused
by builtin words).

>How-To-Repeat:

	not applicable.

>Fix:

	Apply the following fix:

--- sys/boot/common/interp_forth.c.orig	Sun Jan 10 11:41:27 1999
+++ sys/boot/common/interp_forth.c	Sun Jan 10 11:41:50 1999
@@ -23,7 +23,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	$Id: interp_forth.c,v 1.3 1999/01/05 19:07:27 root Exp $
+ *	$Id: interp_forth.c,v 1.4 1999/01/10 02:40:11 root Exp root $
  */
 
 #include <string.h>
@@ -40,6 +40,13 @@
 #endif
 
 /*
+ * Eventually, all builtin commands throw codes must be defined
+ * elsewhere, possibly bootstrap.h. For now, just this code, used
+ * just in this file, it is getting defined.
+ */
+#define BF_PARSE 100
+
+/*
  * BootForth   Interface to Ficl Forth interpreter.
  */
 
@@ -90,17 +97,22 @@
     if (!parse(&argc, &argv, line)) {
 	result = (cmd)(argc, argv);
 	free(argv);
+	/* ** Let's deal with it elsewhere **
 	if(result != 0) {
 		vmTextOut(vm,argv[0],0);
 		vmTextOut(vm,": ",0);
 		vmTextOut(vm,command_errmsg,1);
 	}
+	*/
     } else {
+	/* ** Let's deal with it elsewhere **
 	vmTextOut(vm, "parse error\n", 1);
-	result=1;
+	*/
+	result=BF_PARSE;
     }
     free(line);
-    stackPushINT32(vm->pStack,!result);
+    /* This is going to be thrown!!! */
+    stackPushINT32(vm->pStack,result);
 }
 
 /*
@@ -110,14 +122,21 @@
 bf_init(void)
 {
     struct bootblk_command	**cmdp;
+    char create_buf[41];	/* 31 characters-long builtins */
     int fd;
    
     ficlInitSystem(4000);	/* Default dictionary ~4000 cells */
     bf_vm = ficlNewVM();
 
+    /* Builtin word "creator" */
+    ficlExec(bf_vm, ": builtin: >in @ ' swap >in ! create , does> @ execute throw ;");
+
     /* make all commands appear as Forth words */
-    SET_FOREACH(cmdp, Xcommand_set)
+    SET_FOREACH(cmdp, Xcommand_set) {
 	ficlBuild((*cmdp)->c_name, bf_command, FW_DEFAULT);
+	sprintf(create_buf, "builtin: %s", (*cmdp)->c_name);
+	ficlExec(bf_vm, create_buf);
+    }
 
     /* try to load and run init file if present */
     if ((fd = open("/boot/boot.4th", O_RDONLY)) != -1) {
@@ -136,6 +155,25 @@
     
     result = ficlExec(bf_vm, line);
     DEBUG("ficlExec '%s' = %d", line, result);
+    switch (result) {
+    case VM_OUTOFTEXT:
+    case VM_ABORTQ:
+    case VM_QUIT:
+    case VM_ERREXIT:
+	break;
+    case VM_USEREXIT:
+	printf("No where to leave to!\n");
+	break;
+    case VM_ABORT:
+	printf("Aborted!\n");
+	break;
+    case BF_PARSE:
+	printf("Parse error!\n");
+	break;
+    default:
+        /* Hopefully, all other codes filled this buffer */
+	printf("%s\n", command_errmsg);
+    }
     setenv("interpret", bf_vm->state ? "" : "ok", 1);
     
 }
--- sys/boot/ficl/ficl.h.orig	Sun Jan 10 11:04:10 1999
+++ sys/boot/ficl/ficl.h	Sun Jan 10 11:37:31 1999
@@ -114,6 +114,19 @@
 ** 4. Ficl uses the pad in CORE words - this violates the standard,
 **    but it's cleaner for a multithreaded system. I'll have to make a
 **    second pad for reference by the word PAD to fix this.
+** 5. The whole inner interpreter is screwed up. It ought to be detached
+**    from ficlExec. Also, it should fall in line with exception
+**    handling by saving state. (sobral)
+** 6. EXCEPTION should be cleaned. Right now, it doubles ficlExec's
+**    inner interpreter. (sobral)
+** 7. colonParen must get the inner interpreter working on it's "case"
+**    *before* returning, so that it becomes possible to execute them
+**    inside other definitions without recreating the inner interpreter
+**    or other such hacks. (sobral)
+** 8. We now have EXCEPTION word set. Let's:
+**    8.1. Use the appropriate exceptions throughout the code.
+**    8.2. Print the error messages at ficlExec, so someone can catch
+**         them first. (sobral)
 **
 ** F o r   M o r e   I n f o r m a t i o n
 **
@@ -153,6 +166,9 @@
 
 /*
 ** Revision History:
+** 10 Jan 1999 (sobral) EXCEPTION word set has been added, and existing
+** words has been modified to conform to EXCEPTION EXT word set. 
+**
 ** 27 Aug 1998 (sadler) testing and corrections for LOCALS, LOCALS EXT,
 **  SEARCH / SEARCH EXT, TOOLS / TOOLS EXT. 
 **  Added .X to display in hex, PARSE and PARSE-WORD to supplement WORD,
@@ -470,11 +486,13 @@
 /*
 ** Exit codes for vmThrow
 */
-#define VM_OUTOFTEXT    1   /* hungry - normal exit */
-#define VM_RESTART      2   /* word needs more text to suxcceed - re-run it */
-#define VM_USEREXIT     3   /* user wants to quit */
-#define VM_ERREXIT      4   /* interp found an error */
-#define VM_QUIT         5   /* like errexit, but leave pStack & base alone */
+#define VM_OUTOFTEXT -256   /* hungry - normal exit */
+#define VM_RESTART   -257   /* word needs more text to suxcceed - re-run it */
+#define VM_USEREXIT  -258   /* user wants to quit */
+#define VM_ERREXIT   -259   /* interp found an error */
+#define VM_ABORT       -1   /* like errexit -- abort */
+#define VM_ABORTQ      -2   /* like errexit -- abort" */
+#define VM_QUIT       -56   /* like errexit, but leave pStack & base alone */
 
 
 void        vmBranchRelative(FICL_VM *pVM, int offset);
--- sys/boot/ficl/ficl.c.orig	Sun Jan 10 11:38:55 1999
+++ sys/boot/ficl/ficl.c	Sun Jan 10 10:57:01 1999
@@ -237,6 +237,8 @@
         break;
 
     case VM_ERREXIT:
+    case VM_ABORT:
+    case VM_ABORTQ:
     default:    /* user defined exit code?? */
         if (pVM->state == COMPILE)
         {
--- sys/boot/ficl/words.c.orig	Sat Jan  9 05:23:59 1999
+++ sys/boot/ficl/words.c	Sun Jan 10 10:59:12 1999
@@ -1180,13 +1180,10 @@
     // Get next word...if out of text, we're done.
     */
     if (si.count == 0)
-    {
         vmThrow(pVM, VM_OUTOFTEXT);
-    }
 
     interpWord(pVM, si);
 
-
     return;                 /* back to inner interpreter */
 }
 
@@ -1234,7 +1231,6 @@
             {
                 vmThrowErr(pVM, "Error: Compile only!");
             }
-
             vmExecute(pVM, tempFW);
         }
 
@@ -2441,7 +2437,7 @@
 
 static void ficlAbort(FICL_VM *pVM)
 {
-    vmThrow(pVM, VM_ERREXIT);
+    vmThrow(pVM, VM_ABORT);
     return;
 }
 
@@ -2696,21 +2692,25 @@
 ** When the parse area is empty, restore the prior input source
 ** specification. Other stack effects are due to the words EVALUATEd. 
 **
-** DEFICIENCY: this version does not handle errors or restarts.
+** DEFICIENCY: this version does not handle restarts. Also, exceptions
+** are just passed ahead. Is this the Right Thing? I don't know...
 **************************************************************************/
 static void evaluate(FICL_VM *pVM)
 {
     UNS32 count = stackPopUNS32(pVM->pStack);
     char *cp    = stackPopPtr(pVM->pStack);
     CELL id;
+    int result;
 
     IGNORE(count);
     id = pVM->sourceID;
     pVM->sourceID.i = -1;
     vmPushIP(pVM, &pInterpret);
-    ficlExec(pVM, cp);
+    result = ficlExec(pVM, cp);
     vmPopIP(pVM);
     pVM->sourceID = id;
+    if (result != VM_OUTOFTEXT)
+	vmThrow(pVM, result);
     return;
 }
 
@@ -4046,6 +4046,152 @@
     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.
+ *
+ * BUGS: do not handle locals unnesting correctly... I think...
+ *
+ * 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 +4528,14 @@
     dictAppendWord(dp, "key?",	    keyQuestion,    FW_DEFAULT);
     dictAppendWord(dp, "ms",        ms,             FW_DEFAULT);
     dictAppendWord(dp, "seconds",   pseconds,       FW_DEFAULT);
+    /*
+    ** EXCEPTION word set
+    */
+    dictAppendWord(dp, "catch",     catch,          FW_DEFAULT);
+    dictAppendWord(dp, "throw",     throw,          FW_DEFAULT);
+
+    ficlSetEnv("exception",            FICL_TRUE);
+    ficlSetEnv("exception-ext",        FICL_TRUE);
 
     /*
     ** Set CORE environment query values
--- sys/boot/ficl/softwords/softcore.fr.orig	Sun Jan 10 06:59:34 1999
+++ sys/boot/ficl/softwords/softcore.fr	Sun Jan 10 07:35:38 1999
@@ -33,7 +33,9 @@
     postpone if 
     postpone ." 
     postpone cr 
-    postpone abort 
+    -2
+    postpone literal
+    postpone throw 
     postpone endif 
 ; immediate 
 

>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?199901100300.MAA00872>