Improving Nasal: Difference between revisions

Jump to navigation Jump to search
m (→‎Better debugging/development support: flags - profiling and debugging)
Line 74: Line 74:
* Register a callback for handling errors using call()
* Register a callback for handling errors using call()
* work on abstracting the GC interface (Hooray) {{Progressbar|50}}
* work on abstracting the GC interface (Hooray) {{Progressbar|50}}
* Register a callback for OP_FCALL et al. to be able to time function calls. Example (relies on the gen.nas module and debug.decompile):
* Register a callback for OP_FCALL et al. to be able to time function calls {{Progressbar|80}}. Example [https://gitorious.org/~philosopher/nasal-standalone/nasal-experiments/blobs/extended-f_call/test.nas].
<syntaxhighlight lang="php">
 
# Debugging:
runindebug = func(fn, arg...) {
    var start = []; var indent = "";
   
    call(func, arg, nil, nil, {
          op_fcall:func(context) {
            var obj = debug.decompile(context.fn);
            obj.end = context.ip+1;
            append(start, [get_fcall_lvalue(obj)]);
            print(indent, "Start function call on function ", start[-1][0]);
            append(start[-1], systime()); #save it now for most accurate results
            indent ~= "  ";
          },
          op_return:func {
            var v = pop(start);
            indent = substr(indent, 0, size(indent)-2);
            print(indent, "Took ", systime()-v[1], " seconds");
          },
          error:func(err...) {
            printf(err[0]);
            for (var i=0; i<size(err); i+=1)
              printf("  called from %s line %d", err[i], err[i+=1]);
            print("Stack info:");
            var namespace = caller(1)[0];
            printf("Local namespace:");
            printf("  "~gen.dump_namespace(namespace, 0, 2, "  "));
            var c = caller(1)[1]; var i = 0;
            while ((var cl = closure(c, i)) != nil) {
              printf("Closure %d:", i+=1);
              printf("  "~gen.dump_namespace(cl, 0, 2, "  "));
            }
            var stack = debug.stackinfo();
            forindex (var i; stack) {
              printf("Caller %d:", i);
              printf("  Local namespace:");
              printf("    "~gen.dump_namespace(stack[i].locals, 0, 4, "    "));
            }
          },
        }, var err=[]);
};
 
var get_fcall_lvalue = func(obj, level=0, col=0, suffix="") {
    var output = gen.decompile_expr(obj, level, col, suffix);
    var level = 1;
    for (var i=-2; i>=-size(output) and level > 0; i-=1) {
        if (output[i] == `)`) level += 1;
        elsif (output[i] == `(`) level -= 1;
    }
    return substr(output, 0, size(output)-i);
};
 
# It is really easy to debug code now, just save off every
# expression to a variable and browse them on error.
var possibly_buggy_code = func(arg...) {
    var sum = (var _1 = getprop("/sim/node/not/exists[0]")) +
    (var _2 = getprop("/sim/node/not/exists[1]"));
    return var _3 = sprintf("%8.7f", sum);
};
 
</syntaxhighlight>
* Set breakpoints: register callbacks for values of <tt>(struct Frame*)->ip</tt>.
* Set breakpoints: register callbacks for values of <tt>(struct Frame*)->ip</tt>.
** Typically supported conditional break point types are [http://www.ofb.net/gnu/gdb/gdb_28.html][http://www.delorie.com/gnu/docs/gdb/gdb_29.html][http://winappdbg.sourceforge.net/HowBreakpointsWork.html]:
** Typically supported conditional break point types are [http://www.ofb.net/gnu/gdb/gdb_28.html][http://www.delorie.com/gnu/docs/gdb/gdb_29.html][http://winappdbg.sourceforge.net/HowBreakpointsWork.html]:
Line 145: Line 83:
** '''Parsing:''' Say something other than "parse error", like "null pointer".
** '''Parsing:''' Say something other than "parse error", like "null pointer".
** '''VM:''' Indicate type of variable if wrong type.
** '''VM:''' Indicate type of variable if wrong type.
** '''Both:''' Could we give line ''and'' column?
** '''Both:''' Could we give line ''and'' column? Note: I tried this and failed (even just copying Andy's code I got random numbers sometimes). It's a big patch...
** Most of these diagnostics could be delegated to Nasal space by using some of our C-space hooks that expose the parser/codegen and VM
** Most of these diagnostics could be delegated to Nasal space by using some of our C-space hooks that expose the parser/codegen and VM
* Working with bytecode:
* Working with bytecode:
Line 151: Line 89:
** Decompile to text: partial (untested)
** Decompile to text: partial (untested)
** Optimize: not started (it makes only sense to look at optimizations after we're able to instrument/profile a running FG session to come up with hot spots that are executed either frequently, or that are responsible for significant runtime overhead - i.e. due to GC pressure or other issues)
** Optimize: not started (it makes only sense to look at optimizations after we're able to instrument/profile a running FG session to come up with hot spots that are executed either frequently, or that are responsible for significant runtime overhead - i.e. due to GC pressure or other issues)
** Working with it: provide Bytecode class: not started (the exposeOpcode() API already exists, most other machinery could be built in scripting space on top of it?)
** Working with it: provide Bytecode class in Nasal: not started<!-- (the exposeOpcode() API already exists, most other machinery could be built in scripting space on top of it?)-->
* Inspect Context: not started, should be easy.
* Inspect Context: not started, should be easy.
* Expose Tokens to Nasal: implemented by Hooray as argument to compile(), should be extended to cover output from lex.c and after blocking in addition to the current after-prec-ing (and before freeing!) support.
* Expose Tokens to Nasal: implemented by Hooray as argument to compile(), should be extended to cover output from lex.c and after blocking in addition to the current after-prec-ing (and before freeing!) support.
** compile() being used by call(), it should be straightforward to also map a call() hash.callback to do the same thing here - so that there's no disparity here.
** compile() being used by call(), it should be straightforward to also map a call() hash.callback to do the same thing here - so that there's no disparity here.
** Yeah, just another entry in naContext->extras, right?
* Real function name support via assignment:
* Real function name support via assignment:
** Option 1: look at the parse tree and check if the right side of the assignment is a function. If so, go ahead and parse the function with the left side as the "name" of the function instead of falling through to more recursions of genExpr().
** Option 1: look at the parse tree and check if the right side of the assignment is a function. If so, go ahead and parse the function with the left side as the "name" of the function instead of falling through to more recursions of genExpr().
Line 163: Line 102:
** Option 3: abandon <tt>var foo = func(){}</tt> for ECMAscript-like function declaration syntax <tt>function foo() {}</tt>. This would not affect the use of anonymous func expressions but would instead be applicable in cases where we want to say "this function is static (i.e. permanent) and should have a name" (as opposed the the case of temporary storage variables for functions). Regardless of the method used, a name member will have to be added to naFunc's and the VM and error handling procedures will have to be changed according.
** Option 3: abandon <tt>var foo = func(){}</tt> for ECMAscript-like function declaration syntax <tt>function foo() {}</tt>. This would not affect the use of anonymous func expressions but would instead be applicable in cases where we want to say "this function is static (i.e. permanent) and should have a name" (as opposed the the case of temporary storage variables for functions). Regardless of the method used, a name member will have to be added to naFunc's and the VM and error handling procedures will have to be changed according.
** Regarding the last comment: Providing an API to "lock" a symbol/naRef to become immutable would be generally useful, not just for functions - but also for constants (math.pi FT2M etc) and other stuff that may otherwise break consistency - ThorstenR mentioned a couple of times how he's intentionally replicating standard constants in LW/AW just to be on the safe side, because there's no such thing as a "constant" in Nasal. Providing a library function to make naRefs read-only should be straightforward, and could be easily implemented by hooking into the VM to register a callback that yields naRuntimeError() -aka die()- for any such attempts. The method would be scalable to also implement optional typing or min/max/stepping (value ranges), too
** Regarding the last comment: Providing an API to "lock" a symbol/naRef to become immutable would be generally useful, not just for functions - but also for constants (math.pi FT2M etc) and other stuff that may otherwise break consistency - ThorstenR mentioned a couple of times how he's intentionally replicating standard constants in LW/AW just to be on the safe side, because there's no such thing as a "constant" in Nasal. Providing a library function to make naRefs read-only should be straightforward, and could be easily implemented by hooking into the VM to register a callback that yields naRuntimeError() -aka die()- for any such attempts. The method would be scalable to also implement optional typing or min/max/stepping (value ranges), too
* Timing parts of VM: use the callbacks and systime/unit.time to time things. Need hooks into the GC as well. Statistics worth tracking (also look at similar tools like [http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html google perftools]):
** This should be doable, since I do a naRuntimeError(ctx,naGetError(subcontext)) which ''should'' pass errors from a callback (untested).
* Timing parts of VM: use the callbacks and systime/unix.time to time things. Need hooks into the GC as well. Statistics worth tracking (also look at similar tools like [http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html google perftools]):
** Per function (also handles timers/listeners and other typical FG callbacks):
** Per function (also handles timers/listeners and other typical FG callbacks):
*** ncalls per frame/cumulative
*** ncalls per frame/cumulative
395

edits

Navigation menu