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>
