From owner-freebsd-bugs Sat Jan 9 19:10:09 1999 Return-Path: Received: (from majordom@localhost) by hub.freebsd.org (8.8.8/8.8.8) id TAA19405 for freebsd-bugs-outgoing; Sat, 9 Jan 1999 19:10:09 -0800 (PST) (envelope-from owner-freebsd-bugs@FreeBSD.ORG) Received: from freefall.freebsd.org (freefall.FreeBSD.ORG [204.216.27.21]) by hub.freebsd.org (8.8.8/8.8.8) with ESMTP id TAA19396 for ; Sat, 9 Jan 1999 19:10:06 -0800 (PST) (envelope-from gnats@FreeBSD.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.8.8/8.8.5) id TAA18950; Sat, 9 Jan 1999 19:10:01 -0800 (PST) Received: from peach.ocn.ne.jp (peach.ocn.ne.jp [210.145.254.87]) by hub.freebsd.org (8.8.8/8.8.8) with ESMTP id TAA18972 for ; Sat, 9 Jan 1999 19:01:17 -0800 (PST) (envelope-from dcs@newsguy.com) Received: from daniel.sobral by peach.ocn.ne.jp (8.9.1a/OCN) id MAA29634; Sun, 10 Jan 1999 12:00:42 +0900 (JST) Received: (from root@localhost) by daniel.sobral (8.9.1/8.9.1) id MAA00872; Sun, 10 Jan 1999 12:00:12 +0900 (JST) (envelope-from root) Message-Id: <199901100300.MAA00872@daniel.sobral> Date: Sun, 10 Jan 1999 12:00:12 +0900 (JST) From: dcs@newsguy.com Reply-To: dcs@newsguy.com To: FreeBSD-gnats-submit@FreeBSD.ORG X-Send-Pr-Version: 3.2 Subject: kern/9412: Massive rewrite of loader's builtin error handling Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org >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 @@ -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