VIEWFRAME Mark Zeren Nomad Mediaworks zeren@gradient.cis.upenn.edu ************************************************************************************ This article is reprinted from issue 3.5 (Sept/Oct 1995) of PDA Developers magazine. Copyright(C) 1996 by Creative Digital Publishing Inc. All rights reserved. ************************************************************************************ Rating 5 (on a five-point scale) Pros New additions architecture, function decompiler, fast intuitive navigation. A completely native debugger - a must-have for communications development. Cons Object-display area limited by small Newton screens, no ability to debug in breakloops, expression entry line too small. Price $90, $70 for PDA Developers subscribers, $25 upgrade from 1.0 Creative Digital Inc. 293 Corbett Avenue, San Francisco, CA 94114 415.621 4252, 415.621.4922 (fax) cdi@cdigital.com, 74774.50@compuserve.com I rarely leave home without my Swiss army knife. After all, you never know when you might want to open a bottle of rose for a sunset picnic, or dissect an uncooperative telephone jack. Likewise, my Newton never leaves home without a copy of ViewFrame. Essential for the serious programmer, and at the same time easy to use for the casual hobbyist, ViewFrame is the Swiss army knife of Newton development. Its ability to navigate through the operating system, application software, and persistent soup storage provides a detailed view of the inside of your living, breathing MessagePad. A FRAME WITH A VIEW At its heart, ViewFrame is a browser that allows programmers to navigate, inspect, and modify Newton software and soups. Frames, and other built-in NewtonScript data structures make much of the Newton's runtime environment human readable (see "NewtonScript Objects" on page 13). Whereas the Inspector integrated into the Newton Toolkit (NTK) provides a debugging environment similar to the command line interface of other interpreted languages such as LISP or BASIC, ViewFrame's graphical display is a much more natural and efficient environment for exploring the Newton. The first time you install ViewFrame, you'll probably want to explore a bit under the hood. To start, you need to enter a NewtonScript expression in the entry line at the top of the ViewFrame window (see Figure 1). The popup menu at the left of the entry line provides several good starting points including GetRoot(), GetView ('viewFrontMost), and GetGlobals(). Expressions, such as debug(""), which require parameters, automatically position the text carrot between the quotation marks, one of the many little touches that demonstrate the author's attention to detail. Once you select a starting point using the popup, it appears on the expression entry line. Tapping the Eval button below the entry line compiles and executes the expression returning the result as ViewFrame's current object. While the entry line accepts standard pen input and gestures, a better alternative is the Programmer's Keyboard which is included with ViewFrame and is accessible via a button to the right of the entry line. In addition to offering a choice between standard QWERTY and Dvorak keyboards, the Programmer's Keyboard also includes predefined keys for common NewtonScript keywords such as foreach, begin, and end. There is also a slightly cumbersome Unicode entry mode which gets the job done using a minimum of additional screen space (see Figure 2). ViewFrame 1.1's VF+General addition also provides popup menu shortcuts that allow you to jump directly to application part frames and soups. Your starting expression almost always returns an array or frame which appears in the object listing in the center of ViewFrame's main view. Arrays are listed as you might expect, with each line in the object display showing a slot in the array labeled at the left with the slot index. Similarly, frame slots are preceded by their slot symbol. You can sort frames, making very large frames, especially the root view, much more manageable. ViewFrame 1.1 remembers the sorted state of frames in the current path, a welcome change from version 1.0 that saves time-consuming resorting. ViewFrame does its best to create a one line description of the object to which each slot refers. Often text, debug, and other important slots provide a good human readable description of the object (try evaluating GetGlobals().preferences as an example.) ViewFrame also decodes view system bit fields such as viewFlags and viewForm slots, displaying if possible an appropriate NTK constant (see Figure 1). NAVIGATING YOUR NEWTON Once on your way, navigating around your Newton is somewhat reminiscent of surfing the Web. Tapping a slot in an array or frame takes you to the object to which it refers - quite likely another frame or array. If you start with the base view of an application you might follow its proto chain to discover which system prototype it uses, as well as which methods it implements or overrides. As you proceed, each successive slot symbol or index is added to the current object path which appears above the object listing. Frequently, the path is legal NewtonScript that, if evaluated, returns the current object. At any time, if you go too far, a familiar back button lets you retrace your steps. You can also evaluate expressions in the context of the object, or pass the current object as a parameter to other functions. For example, if the current object is a view, evaluating :GlobalBox() (note the colon prefix) sends the GlobalBox() message to the view, and returns the resulting bounds frame as the current object in ViewFrame. :GlobalBox() is also automatically appended to the path, which makes perfect NewtonScript sense. Similarly, individual slots may be referenced with expressions beginning with a bracket ("[") or period (".") depending on whether the current object is an array or frame. In a deviation from standard syntax, an initial "/" forces the result of an expression to be appended to the path, and causes "%" to refer to the current object. Thus, if the current object is a soup entry, evaluating /EntrySize(%) returns the size of the entry while preserving the current path. Depending on the current object, various useful messages and other functions appear at the bottom of the small popup to the left of the entry line. When the current object is a view, you can quickly :Close() or :Hide() it or get its view children array, among other options. Useful expressions such as :Entry(), :Next(), and /MapCursor(%, nil) are also available for manipulating soup cursors. Built-in expressions are available for view templates, views, stores, soups, and cursors, and ViewFrame's addition mechanism supports further customization of the menu. OF BINARIES AND BYTE CODES Browsing through your Newton's network of frames and arrays inevitably leads to an immediate, binary object, or other self contained data structure. While characters and booleans have simple printable forms that need little explanation, integer immediates take on many different roles. ViewFrame decodes view system bit fields using NTK constants. This is a big time saver - few Newton programmers would recognize 592 as vNoKeys + vFloating + vClickable. Since integers may also represent time, a date string is automatically appended to listings for integers in a reasonable range. Binaries are even more versatile than integers, representing everything from floating point numbers to byte code instructions. In addition to specialized viewers for data types such as strings and pictures, ViewFrame has a default binary object viewer with corresponding columns of hex, ASCII, and Unicode versions of the binary object. Unfortunately not all of the Unicode column is visible in ViewFrame's window - we'll have to wait for those fabled large screen Newtons to resolve this problem. I find the binary display mode particularly useful when working on optimizing large polygons derived from ink input. Dumping the polygon data to the Inspector provides an easy way to document the results of various algorithms for removing redundant points. Often binary objects such as icons are found nested in a frame as part of a more complex data structure. Each icon frame has at least two slots: bounds, containing size and scaling information, and bits, containing the actual bitmap data for the icon. An optional mask slot provides a bit map mask used for highlighting effects. When you encounter an icon in ViewFrame (try evaluating "@330") a specialized viewer indicates its size and shows the icon, the mask, and the highlighted icon. Similar viewers exist for other complex data structures such as sounds. While most of the time ViewFrame's default viewers are more useful than displaying the objects as frames, selecting Alternate from the format menu reveals the underlying frame structure of complex objects. NewtonScript functions are also complex data structures, and ViewFrame's ability to decompile NewtonScript is probably its most impressive feature. Using the parameter information and bytecode instructions contained in a CodeBlock frame, ViewFrame can reconstruct, to a remarkable extent, a NewtonScript version of a function (see Figure 3). While the decomposed function lacks the comments and formatting of the original source code, variable and function names are preserved. With ViewFrame, you can easily browse the source code of the system as well as application software. I quickly discovered the value of this functionality when working on an enhanced scrolling list of items, exemplified by the Prefs application. Understanding how to implement scrolling and overviews while preserving the interface of the built-in protoRoll would have been extremely difficult without access to the protoRoll source code. In effect ViewFrame is a source code browser for the view class hierarchy that Apple left out of the NTK. ViewFrame cannot decompile BinCFunctions which are precompiled with NTK 1.5. On the other hand, ViewFrame 1.1 does provide an ARM disassembler. IN SITU DEBUGGING Early Newton programming pioneers quickly discovered that writing, and especially debugging, communications code was not for the fainthearted. Working around bugs in early communications software was tough enough, and the fact that the serial port was unavailable for connecting to the Inspector only made matters worse. ViewFrame came to the rescue with entirely native debugging. Although many problems have since been cleared up by system and NTK updates, ViewFrame remains a must for anyone writing communications software. The new VF+Intercept addition allows you to easily intercept specific methods of your application's views. Each intercept may optionally perform a number of functions such as entering a breakloop before or after executing the intercepted method, or passing the functions results and context to ViewFrame. There are several different types of trace interrupts which you can use to selectively, and easily, turn tracing on and off for specific methods. You can specify intercepts directly using ViewFrame, or more practically include them in your source code. One of ViewFrame's weaknesses as a debugging tool is it's inability to run during breakloops. The Inspector is currently the only tool that lets you examine your application after it has thrown an exception and fallen into a breakloop. Another disadvantage is that ViewFrame must share precious screen space with your applications. The fact that ViewFrame's window is resizeable and zoomable helps, as does the Delay button which hides ViewFrame for several seconds. Still, ViewFrame begs for a larger screen. I would appreciate an option to iconize ViewFrame, or at least be allowed to move it partially off screen. THE VIEWFRAME EDITOR The ViewFrame Editor, a separate application included with ViewFrame (see Figure 3), allows you to edit, store and evaluate larger chunks of NewtonScript. Alongside ViewFrame and the Programmer's keyboard, the Editor seems to be the ugly duckling of the trio. I rarely install or use the editor, though one or two of the sample code fragments provided are occasionally useful. Currently the links between ViewFrame and the Editor are very limited. I think that there are opportunities to write additions to better integrate the two. VIEWFRAME ADDITIONS My favorite new feature in ViewFrame 1.1 is the Additions architecture. Between 1.0 and 1.1 ViewFrame has gone from a modest package of 92K to a 70K application and a suite of five additions. The first advantage of this new architecture is the ability to optimize your configuration based on your storage constraints and debugging needs. There's no need to install, for example, the VF+Binary addition if you don't need its specialized functionality. Secondly, and more importantly, the additions architecture defines an API for extending and altering ViewFrame. The API is relatively complete and well documented. DOCUMENTATION & SUPPORT ViewFrame 1.1 includes the ViewFrame application, five additions, the Programmer's keyboard, and the ViewFrame Editor. Creative Digital includes demo versions and documentation of several of their other development tools. Documentation comes in the form of a clearly written 50-page manual that describes ViewFrame's functions as well as some of the author's observations on bytecode and object-system implementation. Most of the documentation for the additions is only available on disk. Jason Harper, ViewFrame's author, provides support for his product. He can be reached via CompuServe, telephone, or fax. On the several occasions I had questions, I received prompt and very informative replies. CONCLUSION I am a big fan of ViewFrame. I really can't imagine developing for the Newton with out it. Having it at my side form the beginning helped beat the sometimes steep learning curve of the Newton's unconventional development environment. I fully expect ViewFrame to continue to evolve as the premier native debugger for the Newton. # # # NEWTONSCRIPT OBJECTS For those not familiar with the Newton development environment, NewtonScript provides four primitive data structures, or object types: immediates, binaries, arrays, and frames. Immediates Immediate values are contained entirely in a 32-bit reference value and may represent integers (30 bits), characters (16-bit Unicode), or booleans. Immediate integers are often used as bit fields in the Newton view system. Some example immediates in NewtonScript syntax are: 142; nil; $A; true; Binaries Binary objects represent any block of bytes up to a maximum size of 16MB. Strings (in Unicode), floating point numbers (64 bits), and pictures (Mac PICT format) are all examples of binary objects. Most binary objects also have a class, which indicates the type of data contained by the binary object. Arrays NewtonScript arrays are zero based arrays of slots which contain references to NewtonScript objects. myArray := ["a string", 142]; // array w/ 2 slots myArray[1]; // = 142 Frames Frames are NewtonScript's distinguishing data structure. A frame contains a set of labeled slots: JohnDoe := {name: "John", age: 2}; JohnDoe.age; // = 2 Many of the Newton's internal data structures are represented using frames, and the fact that symbols for frame slots are available at runtime makes these data structures human-readable and easy to navigate. A compiled function, for example, is a frame with slots that contain the literals and parameters used by the function in addition to the actual byte code instructions. Two special slots, _proto and _parent, allow a frame to inherit slot values from a hierarchy of frames. The Newton view system relies on these slots by connecting the prototype hierarchy (defining the view's behavior) to the _proto slot and the view hierarchy (defining sub-view/parent-view relationships) to the _parent slot.