7.1 Debugging Directives in Oolong

A source-level debugger can execute JVM bytecodes and show you the corresponding parts of your original source program. The debugger tries to show you the source code line that generated the currently executing bytecodes. It also shows you the value of local variables, the structure of objects, and other data.

Even after your program is compiled, it is easy for the JVM to show you certain kinds of information. For example, the class file includes the names of classes and the names and types of fields. However, some kinds of information are not directly reflected in the bytecodes. In the JVM, local variables have only numbers, not names. The class file allows you to provide a mapping from the numbers to names. Also, it is impossible to tell which bytecodes came from which line of source code. The class file allows you to provide a mapping from bytecode address to the source line it came from.

These mappings were originally designed to support the Java language, but they can be adapted to support just about any language you might want to create. Almost all languages have the concepts of "line number" and "local variable." Most Java debuggers are willing to show any file at all as the source, even if it's not Java.

The Oolong language has a way for you to fill in the mappings for the class file with the .source, .line, and .var directives. If you compile your program from some other language (like Prolog, Lisp, or Logo) into Oolong and then use an Oolong assembler to build the class file, you can use these directives to provide a link from the class file back to the original source program.

7.1.1 .source Directive and .line Directives

The .source directive names the file that contains the source of this class so that a debugger can show you the source code. This allows you to name the actual source of the file if the Oolong program is an intermediate from something else. If no .source directive is given, then Oolong uses the name of the file it's reading. For example, suppose you've written a file EightQueens.P, a Prolog program. A Prolog-to-Oolong compiler could translate it into the Oolong source EightQueens.j. To connect the .j file back to the original file, the file would start with

 .class EightQueens .source EightQueens.P 

You should include only the actual file name, not the path to the file. It's up to the debugger to locate the source file EightQueens.P when it loads the class file.

The .line directive is used to map Oolong instructions to source code in the original file. The .line directives are interspersed with the Oolong instructions; each instruction after a .line directive is considered part of the same line, until the next .line directive comes along. For example, consider this Logo program in the file DrawAngle.logo:

 forward 60 right 90 forward 20 

Compiled into Oolong, it becomes

 .class DrawAngle .super LogoProgram .source DrawAngle.logo .method public run()V .line 1                        ; forward 60 aload_0                        ; Push this bipush 60                      ; Push 60 invokevirtual LogoProgram/forward(I)V   ; Draw forward 60 .line 2                        ; right 90 aload_0 getfield LogoProgram/theta I   ; Push the angle bipush 90                      ; Push 90 degrees iadd                           ; Add 90 degrees to the angle sipush 360                     ; Compute the remainder of irem                           ; (theta+90)/360 aload_0                        ; Store it back into theta swap putfield LogoProgram/theta I .line 3                        ; forward 20 aload_0 bipush 20 invokevirtual LogoProgram/forward(I)V return .end method 

You can use the same line number more than once within a method. That's particularly useful for control structures like Java's for, where some control code occurs both before and after the enclosed code:

 for(i = 0; i < 10; i++) {    // Do stuff } 

If the for loop appears on line 10 of the source file, then this becomes:

 .line 10             ; Begin at the beginning loop_begin:    iload_1           ; Push i    bipush 10         ; Push 10    if_icmpge break   ; If greater than 10, break out of the loop .line 12    ;; Do stuff .line 10             ; Now we're back doing stuff from line 1    iinc 1 1          ; Increment i    goto loop_begin   ; And go back to the beginning of the                      ; program .line 14             ; Now we're outside the loop break: 

7.1.2 .var Directive

Although fields of objects are referenced in bytecodes by name, local variables are referenced only by number. The .var directive is used to assign names to the variables. Naming variables is entirely optional. The names aren't used by the JVM itself; they're only used by the debugger.

The format of the .var directive is

 .var number is name type [ from label1 to label2 ] 

where number is the number of the local variable, name is the name that you want to give it, and type is the type that the variable holds. For example, in this Java program fragment,

 public void main(String args[]) 

you might use the .var directive

 .var 0 is args [Ljava/lang/String; 

The from part of the .var directive is optional. A particular local variable slot may have different names at different parts of the program. The Oolong language uses label1 and label2 to indicate the range where a variable is given a particular name. The same variable may have different names or different types in different ranges. For example,

 static void fahrenheitToCelsius(float fahrenheit) {    float celsius = (fahrenheit - 32) * 5 / 9;    System.out.println(celsius); } 

You never need both fahrenheit and celsius at the same time, so they can both be stored in the same local variable. For part of the computation, that variable is named fahrenheit, and for the rest, it's named celsius. Translated into Oolong, this method is

 .method static fahrenheitToCelsius (F)V .limit stack 2 .var 0 is fahrenheit from begin to end_of_computation .var 0 is celsius from end_of_computation to end begin:    fload_0                 ; Push fahrenheit in variable 0    ldc 32.0                ; Subtract 32    fsub    ldc 5.0                 ; Multiply by 5    fmul    ldc 9.0                 ; Divide by 9    fdiv end_of_computation:    fstore_0                ; Now variable 0 is celsius    getstatic java/lang/System/out Ljava/io/PrintStream;    fload_0                 ; Print variable 0    invokevirtual java/io/PrintStream/println (F)V    return end: .end method 

It's also possible in Java for two different variables to have the same name in different parts of a method:

 {    int i;    /* i is variable 1 */ } {    int j;    int i;    /* Here, j is variable 1 and i is variable 2 */ } 

To let the debugger know which variable is named what, use this Oolong code:

 .var 1 is i I from scope1begin to scope1end .var 1 is j I from scope2begin to scope2end .var 2 is j I from scope2begin to scope2end scope1begin:    ;; Here variable 1 is i, and variable 2 is unnamed scope1end:    ;; ... scope2begin:    ;; Here, variable 1 is j, and variable 2 is i scope2end: 


Programming for the Java Virtual Machine
Programming for the Javaв„ў Virtual Machine
ISBN: 0201309726
EAN: 2147483647
Year: 1998
Pages: 158
Authors: Joshua Engel

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net