F.G.H. 3/14/85 LispCourse #13: Error Handling and the Break Package LispCourse #13: Error Handling and the Break Package Background Concept: The Stack Consider the following set of functions: (DEFINEQ (LoadMany (LAMBDA (LoadList) (COND (LoadList (LoadOne (CAR LoadList)) (LoadMany (CDR LoadList)))))) (LoadOne (LAMBDA (File) (LOAD File 'PROP)))) Trace the following function call down to the first call to LOAD: (LoadMany '(FOO BAR)) (NIL ((.05 13.0 NIL) (TEXT (24.0 . 168.0) ("LoadMany: LoadList _ (FOO BAR)" "") 1.0 (LEFT BASELINE) (HELVETICA 12) ((24.0 171.0 254.0 13.0) (24.0 158.0 0.0 13.0)) NIL)) ((.05 13.0 NIL) (TEXT (48.0 . 136.0) ("LoadOne: File _ FOO") 1.0 (LEFT BASELINE) (HELVETICA 12) ((48.0 133.0 161.0 13.0)) NIL)) ((.05 13.0 NIL) (TEXT (80.0 . 112.0) ( "LOAD: FILE _ FOO LDFLG _ PROP") 1.0 (LEFT BASELINE) (HELVETICA 12) ((80.0 109.0 279.0 13.0 )) NIL)) ((.023 135.5 NIL) (BOX (72.0 104.0 271.0 23.0) 2)) ((.071 167.5 NIL) (BOX (32.0 88.0 335.0 71.0) 2)) ((.191 187.5 NIL) (BOX (8.0 8.0 375.0 191.0) 2))) (0 0 385.0 201.0) 1.0 8.0 When the Lisp evaluator starts to process the call to LOAD, it is keeping track of three function calls: the call to LoadMany, the call to LoadOne, and the call to LOAD. The Lisp evaluator needs to keep track of lots of information about these three function calls. For example for each function call the evaluator must record the exact function called, the values of the arguments in the function call, where in the function body it currently is, and so on. Lisp keeps this kind of information in a data structure called the stack. The stack consists a set of stack frames. Each stack frame contains the information about one function call. The stack frames are ordered from most recent at the top of the stack to oldest at the bottom of the stack. When a new function call is made, a new frame is added to the top of the stack. When a function call completes, its frame is removed from the top of the stack. Thus the stack grows and shrinks as function calls are made and then completed. For example, at the start of the LOAD function call the Lisp stack would look something like: LOAD: FILE _ FOO LDFLG _ PROP LoadOne: File _ FOO LoadMany: LoadList _ (FOO BAR) After FOO is loaded and LoadOne completes, the Lisp stack would be: LoadMany: LoadList _ (FOO BAR) LoadMany then recurses to load BAR, so at the point that LOAD is called again the stack would be: LOAD: FILE _ BAR LDFLG _ PROP LoadOne: File _ BAR LoadMany: LoadList _ (BAR) LoadMany: LoadList _ (FOO BAR) Note that at each point, the stack contains an ordered list of the function calls that are in progress. Each function call on the stack is waiting for function call just above it to complete and return a value. The top of the stack contains information about the function call currently being processed. In short, at any given time the stack contains a record of the current state of a computation in progress. When an error occurs, the stack contains valuable information about the state the computation was in when the error occured. Errors: Break or Abort? When the Lisp evaluator encounters an error that DWIM/CLISP cannot "correct", it does one of two things: 1) it aborts the computation in progress. Unless the aborted program specifies otherwise, control returns to the Lisp exec. (Programs can be written so that they "handle" the abort by "fixing' or ignoring the error.) 2) it suspends the computation in progress and enters a break. From the break, the user can "fix" the error and resume (or abort) the computation. The decision whether to Abort or Break is based on the heuristic that complex computations should be broken while simple computations should be aborted. This heuristic rests on the assumption that simple computations are easier redone rather than fixed and resumed. "Complex" in this case is based on two factors, "depth" of the computation and time spent so far in the computation. Depth is basically the number of number of function calls on the stack when the error occurs. If this number passes a given threshold (i.e., the value of HELPDEPTH), the computation will be broken. Time is measured by how long the computation has been in progress. If this time passes a given threshold (i.e., the value of HELPTIME), then the computation will be broken. If neither the depth nor the time passes threshold, then the computation will be aborted. Some programs choose to handle their own aborts using the error processing machinery provided by Interlisp. The computation of "complexity" for these programs is slightly different. The effect is that breaks are less likely to occur and aborts are more likely to occur. When the abort does occur it is up to the program to decide what to do. When an error causes an abort, the program can try to "fix" the error or decide to ignore it. If it does either of these, the computation will proceed as if no error had occurred (though the program will probably print out some sort of error message to let you know that the error did occur.) The program can also decide to force a break or it can force a "true" abort and return to the Lisp exec. Standard packages like TEdit and DEdit almost always process their own aborts. They try to ignore or fix all but the most drastic errors so that the user isn't constantly bombarded by breaks and/or aborts. When a computation is aborted, an error message is always printed in its TTY window before control is given to the Lisp exec. This message usually includes the type of error and the offending object. For example: "Non-numeric arg: NIL" indicates that one of the arguments to some function was NIL when it was to supposed to be some numeric value. There are some 50 standard types of error in Interlisp. We will review some of these later. Examples: 12_(PLUS T) NON-NUMERIC ARG T 13_(QQQQQ 4) UNDEFINED CAR OF FORM QQQQQ 14_(SETQ NIL 5) ATTEMPT TO SET NIL OR T 5 Breaks: the Break Exec and Break Windows When a Break occurs, the computation is suspended and a Break window is opened on the screen. The title of this window is a message describing the error that caused the break. This error message is also printed in the window. A Break exec is started in the Break window. A Break exec is very similar to the Lisp exec except it has an additional set of commands that can be used to "handle" the break. The Break package is primarily intended as a debugging tool for programmers. Rather than aborting a computation when an error occurs, Interlisp suspends the computation and enters a break. The break gives the user an opportunity to examine the suspended computation, make changes that "fix" the error, and then restart the computation from any point. From a programmer's point of view this is a fantastic opportunity. Unfortunately, to make full use of breaks you need to have considerable expertise in Interlisp programming. The average user doesn't have sufficient expertise and therefore can't really take advantage of the power of breaks. But, when an error occurs a break usually occurs. So the all users have to learn to deal with breaks as best they can. We will cover "dealing with breaks" today and pick up the "effective use of breaks" later in the programming part of the course. What is a break and what is it good for? When a break occurs the ongoing computation is suspended at the point at which the error occurred. The break provides the tools for examining the state of the computation including its stack, all of the variables its using, the definitions of the functions it calls, etc. Functions can be edited, the values of variables reset, previous events undone, and so on. The computation can then be restarted at any point or it can be aborted completely. Basically, a break is an opportunity for the user to do what she can to recover from errors, thereby saving any work that has been done by the computaion before the error occurred. The decision to abort the computation if the work can't be saved or isn't worth savingis left in the hands of the user rather than the system. Examples: If during a COPYFILE you run out of space on your account on the file server, the system will enter a break with a message saying "File system resources exceeded". From the break, you can delete some files on your account to free up some space. You can then type "OK" to restart the COPYFILE. If you can't find any files to delete and just want to stop the COPYFILE, you can type "^" to abort the COPYFILE and return to the Lisp exec. If you type "(LOAD 'FOOBAR)" to the Lisp exec and FOOBAR doesn't exist, the system will (sometimes) enter a break with saying "File not found FOOBAR". If you really meant to load FUZBAL, you can type "(SETQ FILE 'FUZBAL)" followed by "OK". This will "correct" the typo you made and restart the computation, causing the file FUZBAL to be loaded. The Break Exec The Break exec running inside the Break window is a read-eval-print loop just like the Lisp exec. The type-in prompt is a ":" rather than a "_" to distinguish the Break exec from the Lisp exec. The history event numbers that preceed the prompt are shared between the Lisp exec and the Break exec, i.e., there is only one history list that serves all execs, Lisp and Break alike. The Break exec contains all of the functionality of the Lisp exec including TTYIN and the Programmer's Assistant. You can evaluate any Lisp expression, CLISP expression, P.A. command, etc. just as you can in the Lisp exec. The Break exec contains a number of additional commands for managing the Break, for repairing the computation, and for restarting/aborting the computation. These commands are like the P.A. commands in that you simply type them into exec followed by a . No parentheses etc. are necessary. BRKEXP: When you enter a break, the value of the variable BRKEXP is the S-expression that caused the error in the evaluator. If you can't figure out what to do from the error message printed in the break window, you can always examine the value of BRKEXP. BRKEXP can also be used as the expression to evaluate when leaving the break. Several of the break exec commands involve modifying or re-evaluating the BRKEXP expression. Example: In the LOAD example above, the value of BRKEXP was (OPENFILE FILE ACCESS RECOG BYTESIZE). This suggests that the variable FILE is the "cause" of the error and should be set to the correct file name. Hence to fix the error, you SETQ FILE to the correct name. You then use the OK break command to exit the break and reevaluate the BRKEXP expression. 41_(LOAD 'FOOBAR) FILE NOT FOUND FOOBAR (OPENSTREAM broken) 42:BRKEXP (OPENFILE FILE ACCESS RECOG BYTESIZE) 43:FILE FOOBAR 44:(SETQ FILE 'FUZBAL] FUZBAL 45:FILE FUZBAL 46:OK {DSK}FUZBAL 47_ Break Exec Commands: The following are special commands that can be used to manage breaks from the break exec. Exiting from a break: When exiting from a break, you return some value. This is used in by the evaluator as the value of the expression that caused the error. For example, the expression (SETQ A (PLUS 1 T)) will cause a "non-numeric arg" break while evaluating the (PLUS 1 T) expression. The value returned from this break is used in place of the value of the (PLUS 1 T) expression and hence is the value that A will be set to. The following functions exit from a break, returning some value. GO Evaluates the BRKEXP expression, prints the result in the break window, and exits the break returning the evaluation result. OK Like GO, except doesn't print the result of the evaluation. RETURN Form Evaluates Form and then exits from the break returning the evaluation result. The following function, exit from a break by aborting and hence doesn't return any value: ^ exits the break by causing an abort. This will cause a return to the Lisp exec unless aborts are explicitly processed by the broken program as described earlier. Other Commands: Many of the break exec commands rely on the value of the variable LASTPOS. LASTPOS points to an entry on the stack, i.e., to a function call somewhere on the stack. Many functions allow you to see various characteristics of the LASTPOS function call. You can move the value of LASTPOS in order to examine various function calls on the stack. LASTPOS starts out at the expression causing the break. EVAL evaluates the BRKEXP expression but does not exit from the break. !VALUE is set to the result of the evaluation. Can be used to see what the break would return, if GO or OK were used. @ Atom moves LASTPOS to the most recent call to the function named by Atom. To search for the second most recent call to the function named by atom use "@ Atom Atom" and so on. ?= prints out the values of the arguments to the function at LASTPOS. For example, in the LOAD break described above with LASTPOS pointing to the OPENFILE function call, ?= would print out the values of FILE, ACCESS, RECOG and BYTESIZE. BT DUMMYFRAMEP prints out the names of the function calls on the stack starting from the most recent to the oldest (called a backtrace). The DUMMYFRAMEP is optional, but gets a more concise backtrace with less junk in it. REVERT removes from the stack all function calls from the error back through the function call specified by LASTPOS. It then causes a break in the function call specified by LASTPOS. Thus REVERT allows you to back the computation up to some previous point before the error was encountered. EDIT calls DEdit on the function containing the BRKEXP expression. For example: if in the definition of the function FOO there is an expression like (PLUS 1 T), a break will occur with the value of BRKEXP being (PLUS 1 T). In this break, EDIT will call DEdit on the function FOO because FOO is the function that contains the incorrect statement (PLUS 1 T). Examples of Break Exec in use 99_(COPYFILE (QUOTE {DSK2}ABC) (QUOTE {DSK2}XYZ)) FILE NOT FOUND {DSK2}ABC (OPENSTREAM broken) 100:BRKEXP (OPENSTREAM FILE ACCESS RECOG BYTESIZE PARAMETERS) 1:BT DUMMYFRAMEP OPENSTREAM ERRORSET COPYFILE EVAL LISPX ERRORSET EVALQT ERRORSET T 2:?= *FILE* = {DSK2}ABC *ACCESS* = INPUT *RECOG* = NIL *BYTESIZE* = NIL *PARAMETERS* = ((SEQUENTIAL T)) 3:(COPYFILE '{DSK2}AAA '{DSK2}ABC] {DSK2}ABC.;1 4:OK OPENSTREAM {DSK2}XYZ.;1 5_ ---------------------------- 64_(DEFINEQ (EXAMPLE (LAMBDA (A B C) (PLUS (EXAMPLEX A) (EXAMPLEX B) (EXAMPLEX C))))) (EXAMPLE) 65_(DEFINEQ (EXAMPLEX (LAMBDA (X) (EXAMPLEY X 1)))) (EXAMPLEX) 66_(DEFINEQ (EXAMPLEY (LAMBDA (P Q) (PLUS P Q)))) (EXAMPLEY) 67_(EXAMPLE 2 3 4) 12 ... 77_(EXAMPLE 1 2 T) NON-NUMERIC ARG T (PLUS broken) 80:BT DUMMYFRAMEP PLUS EXAMPLEY EXAMPLEX EXAMPLE EVAL LISPX ERRORSET EVALQT ERRORSET T 81:?= *ARG1* = T *ARG2* = 1 82:@ EXAMPLEY EXAMPLEY 83:?= P = T Q = 1 84:@ EXAMPLEX EXAMPLEX 85:?= X = T 86:REVERT EXAMPLEX (EXAMPLEX broken) 87: BT DUMMYFRAMEP EXAMPLEX EXAMPLE EVAL LISPX ERRORSET EVALQT ERRORSET T 88:?= X = T 89:X T 90:(SETQ X 55) 55 91:X 55 92:BRKEXP (PROGN (* fgh: "13-Mar-85 22:54") (EXAMPLEY X 1)) 93:EVAL EXAMPLEX evaluated 94:!VALUE 56 95:OK EXAMPLEX 61 96_ ---------------------------- Break Windows When a break occurs, the system opens a break window and starts up the break exec within this window. All interaction with the break exec takes place in this window, just as all interaction with the Lisp exec takes place in the TTY window. Break windows provide two other functions besides exec type-in: 1. menu-based access to many of the break exec commands 2. visual displays of the stack and stack frames Menu-based access to the break exec: Clicking on the middle mouse button anywhere in the break window will bring up a menu consisting of 9 items: !EVAL, EVAL, EDIT, revert, ^, OK, BT, BT!, ?=. AzDDDDDDDDDDDDDDDDDDDDDDDDDD@@DDADDDADD" D" DDDDDD@ D DDDD@D@@DOADDHADH O DHADDHADH O D@DD@D@@DO?DDH!DHODHDDHDH!OD@DD@D@@D@DD@DK8MDPDH|QDDH@QDHD!H8 D@DD@D@@D@DD@DBGDJDDBDBBD@DD@D@@DCDDD" DH@HDH@DDH DD"CD@DD@D@@DODDH DH ODH DDH DH OD@DD@D@@DODDH DH ODH DDH DH OD@DD@D@@DFDDIDIBDDDDDD@DDDDD@DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD Choosing EVAL, EDIT, revert, ^, OK, or ?= from the menu simply carries out the corresponding break exec command. For example, choosing the OK entry will exit the break after evaluating the BRKEXP expression. Choosing the BT command from this menu will bring up a small window (called the backtrace window) attached to the left or right edge of the break window. The stack backtrace will be printed in this window, one item (i.e., function call) per line. The window is scrollable if the whole backtrace doesn't fit in the window. ??UUUUUUUWEw>ߪdDE߼|?ߪdDo߽UUUUUUUWqo}߻~}=~UUUUUUUWs=qE߻߻~۽[ߪJR@dDE߻;?~w۽ߪJR @dD}~ݿUUUUUUUWs8@qg?m~m_UUUUUUUWR@qEo߮߻ߪJR@dDE|;?ߪJL<@dD@?=<%UUUUUUUWqUUUUUUUWqEdDE??sđdDJ qJ qEsdDERdDJqKыqEdDEdDqD qEEPPdDEmPPdDmPPq+qE:0dDE>dD |Dq @!"DqE @!"DdDE xR!"DsgDdD @R!"DJ%lq @2!"DJ%|qE @2!"Dsa9TdDE>|8B)dDB%qCa%qEdDEdDDp@q"HADs=qE"PADJR@dDEpAGJR @dDH 2$Hs8@q"H@CHR@qE"DB$HJR@dDEDB'JL<@dD@qqEdDE3dDJQ!qJQ!qEK!dDEJ!dDJ!q2qEdDEdDqDqEEPdDEmPdD<|珄"mPq"@H 6+qE"@H 6ać:0dDE"x *ɒ%>dD<@ *&q @*%qE @H*$dDE |O"G9dDRqqE`dDEPdDPq9qEdDEdDqs=qEJR@dDEJR @dDs8@qR@qEJR@dDEJL<@dDqqEdDEDgdDEPqmPqEmPdDE+dD:0q>aqE@dDE dDqs=qEJR@dDEJR @dDs8@qR@qEJR@dDEJL<@dDqqEdDEdDqqEDEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD The !EVAL and BT! are simply specialized versions of the EVAL and BT commands. The backtrace window and the frame inspector: Each item (i.e., function call) in the backtrace window is individually selectable by clicking on the left or middle mouse buttons while the cursor is over the item. The selected item will be highlighted with a gray background. Selecting an item in the backtrace window has two effects: 1. LASTPOS is set to the corresponding stack entry. This is the menu-based equivalent of the @ command in the break exec. 2. a stack frame inspector is opened on the corresponding stack entry. This stack frame inspector contains the name of the function call and the functions arguments and their values. ;UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU>wUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUw}ãսUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU{}UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUߍw}UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU>wyUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU`߄UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"|qH"@!H@!x!@!H@!H@!@q"|q!Gpp6@!!$H6@!!%H*x!!x *@!D"D *@!!$>D*@!!$H"D"@q!O"xp @!G"D@!$"D@!%(@!( @D @!$@@!$H"|!O"  "DPD "dPD "d<"T "T "L "L>D`` P!PH"Q" PH"Q" FQA H H  >`` >|H BH PB <PB  B  BH  BO> ##??߀?|UUUUUUU_Ȁ9߽߿߿y~UUUUUUU_Ȁ#{￯{>#￯w{8;>{ww{纷{UUUUUUU_)@Ȁ߿ww{ۿUUUUUUU_(@Ȁ#??%p#~ڿ%A@߽]wUUUUUUU_)@Ȁ}w~UUUUUUU_&xȀ#?~?{xK#UUUUUUU_ȀUUUUUUU_"Ȁ#F#Ȁ?BȀ##"#ȀȀ## @ȀڠȀ#ڠ#WtaȀ$}Ȁ#@#>8#@$B D @$B D Ȁ@BTJ""@2!"TI"Ȁ@"Db"s9 Ȁ#$ # $%#8Ȁ Ȁ#$ #"s! &Ȁ*Ȁ#*#2{")@Ȁ(@Ȁ#%p#%A@)@Ȁ&xȀ##Ȁ πȀ#"#ڡ"ڡ"ȀW"Ȁ#ta"#$|Ȁ@Ȁ##{)@Ȁ(@Ȁ#%p#%A@)@Ȁ&xȀ##ȀȀ##ȀȀ""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" The stack frame inspector is a standard inspector window. It has lots of functionality which will not be covered here. However, there are several operations in the inspector that may be useful. In particular: To call DEdit the function in the frame: First select the function name at the top of the inspector window by placing the cursor over the name and clicking the left mouse button. Then click the middle mouse button anywhere in the inspector window. This will bring up a menu with three choices. Choose the middle one: DisplayEdit. This will bring up an editor on the function in the inspector window. To change the value of one of the arguments: First select the argument to be reset by placing the cursor over the argument name and clicking the left mouse button. Then click the middle mouse button anywhere in the inspector window. This will bring up a menu with one item. Choose this item: SET. This will bring up type-in window just above the inspector window. Type the new value for the argument into this window. NOTE that the expression you type in will be evaluated before the argument is set. Hence to set the argument to A, you have to type in (QUOTE A) and so on. GDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD@DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD@_@_@X@X@|'> @  pO |@@@d `@`E$H @cË @$HD$E@ P A  @XÈ Dp#@$>@PC p>|@X@@@X@X 8@@! @!a,XpÇXpa@ÆX! 2d d`  @I@X!"@@  "@Ȁ@!"@ @@  >@! 2@ @` "@$IX!,@p@pa"|hÆ@X @ X@X@@ X@X@ @X@X@X@X@_@_UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUV@yw_UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUV@_UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUV@wz~{o_~`=UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUV@_~moUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUV@~oy~w_~%x}UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUV@_UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUV@_@X@X8@X"D@"D DX DB@X"DB@"DB8BX@X@XAw"8 C@X_vM$D D@@_vM(D D@Cv>8 X_v$$@X_v$ !$@@_v"@!$@_p`"|!ÀX @X@X>8"8!'@XDB$D! @@DB(D @DB8 AAXDB$@@XDB$ @DB"@!8"|!X @X@X 8q$G>xÈ @X $B H"D! * @ DB  "DQ! @ a @HD! *@X B x! @@X DB@"@QA @@ $B "@! @>8q$G@# X @X@X >8G|q'@X H@$E@ H@$E<Hx@X H@@@X Ȅ@d@@ Ȅ@d@ 8G@p'X@X@X<8@@X"D@@"D @"0  @X"@dH| @@X"Dȑ @@"DH @<8O@X@@X@X<|q@G@X"@@B@"@Bw>@_~yϿw;@w}}w~UUUUUUUx}}wCgUUUUUUUx83_}w=ջ)J@_w~})B@?ww~UUUUUUUx9.3{w~UUUUUUUx)* _ww[~)J@_~{Ͽw<=߽g3@@}p ?C_UUUUUUUxUUUUUUUx_@_Dx)* X!$H"Dx)J@X!O"xpx3@ xxXxDDDDDDF@Xxc9:@yxXxV\DF@XxH@yYxc H;XxDDDDDDF@Xx@xxIXxU@Xx@ Ȏ>xB @x!$ D`@xXA$ D` Hq`@x@XA'x "P@x#@A P"`@xA H"P@xXA D "H@x@X ȎD Dqx@ x%)x!/Xx@Xx@px% x Xx@Xx@!xAx83Xx)J@Xx)B@x9.3x)* Xx)J@Xx3@xxXx@XxI|@xU x Xx @Xx @x x#Xx@Xx@xx83Xx)J@Xx)B@x9.3x)* Xx)J@Xx3@xxXx@Xx@xx_@_@DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD@DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD@DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD@DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD@ The new setting for the argument will be displayed immediately in the inspector window. """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""/""/"{?UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUWϼ߻UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUW"/""/g|~"VsUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUW߿_=UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUW"/""/~6"}UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUWϼ}UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUW"/?~>""/"UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUWUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUW"/"","",x"","D""DQ"DQ","x!"","@!""@!@!π","","?``", v;@q"",/ݻ&{H" "/ݻ&{H"@!ûV{F",/׻V{A @"",/ۻV{H!"/ݻV{H /v80@>q",? "",?``"?``",|q"","@!H" ""@!H"P@"x!F ","@!A @"","@!HPA""@!H B@q>C","",``"@",8#8€ "",@@$B D" "@@$B D" @B| "","H@CH ""DB$H DB' @",@ @"",@"",>8 #"", DB !" DA@!<0A@", @"", D@a" D@a>8@#","","","","","","","","","","","","","","","","","","","","","","","",""/""/""/x~?|~}UUUUUUU|""/<~~~sϝUUUUUUU|"}w{o{g"/woosUUUUUUU|BR""/g~ognUUUUUUU|BR"[;ooms=omojB"/~w߭ogUUUUUUU|BR""/=osϞyoUUUUUUU|zRd">?|/"/UUUUUUU|""/UUUUUUU|s$"JPU$O<@! &H<",@! &H<"",x! %H<3?#"@! %H"