[Next] [Previous] [Top]

"How to Use the Vortex Compiler and Environments"

4 Using the Cecil Evaluator


The Cecil evaluator functions like a Lisp read-eval-print loop. You can type Cecil code to be evaluated at the Vortex> and debug> prompts; there also is a stand-alone Cecil read-eval-print program named cecil that can be run. The Cecil code is evaluated and any results are displayed. New global variables, objects, and fields can be declared, and new methods can be added and existing methods can be replaced by entering method declarations to the evaluator. Expressions and declarations can span multiple lines, as long as open brackets, parentheses, or braces remain unmatched at line boundaries. For speed, the evaluator invokes precompiled code wherever possible, and interprets expressions otherwise.

Predicate object and precedence declarations are not currently supported by the evaluator, and typechecking is not performed (type and signature declarations are ignored). Top-level let declarations made in the evaluator create global variables available to the evaluator and interpreted methods (but not the pre-compiled code) in the same run of the program. (As with regular Cecil programs, module declarations are parsed but ignored.)

The evaluator is extremely useful for debugging programs. The ability to redefine methods is especially helpful when debugging, since a corrected method can simply be typed into the program and tested without having to wait for the program to be recompiled. However, there are some situations in which it does not work. Interpreted methods are invoked only through dynamically dispatched message sends. Therefore, if a message send is either inlined or statically bound (and thus does not go through the dynamic dispatching mechanism), then any new or replacing interpreted methods won't be invoked at that call site. Similarly, breakpoints are implemented only for methods called through dynamically dispatched call sites.

With some knowledge of the optimizations performed by the compiler, it is possible to predict whether a particular call site will be dynamically dispatched, and thus a call site at which an interpreted method could be invoked. However, it is likely that a casual user will not want to figure this out. A simple rule is that if a call site is located in a file which was compiled without optimization, then the call site will be dynamically dispatched and thus interpreted methods will perform as expected. Alternatively, if a file includes the (**debug**) pragma, then all methods in that file can be replaced by an interpreted method (i.e., all calls to the methods defined in the file will be forced to be dynamically dispatched). Individual methods can also be annotated (**debug**) as follows:

method testing(x@:int, y:string) (** debug **) { method_body }

Interpreted methods only persist for as long as the program is running. If you add a method to a program, and then exit the program and restart it, the new method will not be present. Interpreted methods are not saved in Vortex checkpoint files either.