Class Models for Lua

# Code Inspector

## `loop.debug.Inspector`

Class of objects that implement a command-line prompt to access the lexical scope of a point in an application. The command-line prompt also allows to navigate through active co-rotines and access the lexical scope of the point they are currently executing. It is also possible to navigate to inactive functions in order to access the current value of their upvalues. This class is useful for implementation of command-line debugging mechanisms.

Each object instance implements an environment that retrieves the value of variables and upvalues of a given point in the code. The method `stop` sets up the environment to access the lexical scope of the point it was called. The access is provided by an interactive prompt similar to the one provided by the Lua stand-alone interpreter. Although it provides additional commands to navigate to different points of the running application like active co-routines.

## Behavior

### Fields

`active`
  Defines if the inspector instance is active or not. If this field evaluates to `false` then all calls to method `stop` have no effect. The default value for this field is `true`.
`input`
  Defines the object used to read commands inserted through the command prompt. The object must provide the method `write` like Lua file handlers. The default value for this field is the standard input (i.e. `io.stdin`).
`viewer`
  Defines the object used to print out messages of the command prompt. The object must provide the same API of instances of `Viewer`. The default value for this field is the `Viewer` class itself.

### Methods

`allbreaks()`
  Return an iterator that iterates over all registered break point marks. The iteration variables are the source name pattern and the source code line.
`removebreak(file, line)`
  Unregisters the break point marks from line `line` of all Lua chucks which source name ends with the string `file`.
`setbreak(file, line)`
  Registers a break point mark in line `line` of all Lua chucks which source name ends with the string `file`. When the execution of the program reaches a break point mark the inspection console is open in that point.
`stop([level])`
  Executes the command-line prompt used to inspect the current executing code. Parameter `level` defines the stack level of the function to be inspected. Level 1 is the function that called `stop`, level 2 is the function above, and so on. The command-line prompt executes Lua code as if the code was executed inside the function being inspected. Additionally it also provides the commands described below. As a shortcut, the commands can be invoked without arguments by typing its name only.
  `see(...)`
    Command that writes the textual representation of all its arguments. This command uses the object in field `viewer` to generate the textual representations.
  `loc([name, value])`
    Command used to access the local variables avaliable in the inspected scope. When used with no arguments it writes the textual representation of all local variables avaliable. If first argument is provided it just returns the value of the local variable with name `name`. If both arguments are provided then it changes the value of the local variable with name `name` to the value of `value`. If there is no local variable with that name `name` the call has no effect.
  `upv([name, value])`
    Command used to access the upvalues avaliable in the inspected scope. When used with no arguments it writes the textual representation of all upvalues avaliable. If first argument is provided it just returns the value of the upvalue with name `name`. If both arguments are provided then it changes the value of the upvalue with name `name` to the value of `value`. If there is no upvalue with that name `name` the call has no effect.
  `env([name, value])`
    Command used to access the environment of the inspected scope. When used with no arguments it writes the textual representation of all global variables avaliable. If first argument is provided it just returns the value of the global variable with name `name`. If both arguments are provided then it changes the value of the global variable with name `name` to the value of `value`. If there is no global variable with that name `name` a new variable is created with the given value.
  `lua([name, value])`
    Command used to access the default Lua global environment, i.e. the default value of `_G`. When used with no arguments it writes the textual representation of the default Lua global state. If first argument is provided it just returns the value of the global variable with name `name` from the default Lua global environment. If both arguments are provided then it changes the value of the global variable with name `name` to the value of `value` in the default Lua global environment. If there is no global variable in the default Lua global environment with that name `name` a new variable is created with the given value.
  `goto(where)`
    Command used to change the inspected scope to the co-routine or function defined by argument `where`. When nagivating to a function, no local variables are accessible. To access local variables you have to nagivate to a co-routine executing the function and go up in the stack level until reach the desired function. It is not possible to navigate to the main thread.
  `goup()`
    Command used to change the inspected scope to the function one level up in the stack of the current thread.
  `back()`
    Command used to go back to the previous inspected scope.
  `hist()`
    Command that list all the scope changes already performed through the commands `goto` and `goup`.
  `curr()`
    Command that shows the upper levels of the stack of the current inspected environment.
  `done()`
    Command used to indicate the conclusion of the inspection and continue the execution.
  `step([kind])`
    Command used to indicate the conclusion of the inspection and re-open the inspection console after execution of one more instruction of the source code. If the parameter `kind` is `"in"` and the next instruction of the source code results in a function call then the inspection console is re-open at the first instruction of the called function's source code. Additionally, if the parameter `kind` is `"out"` then the rest of the current function is executed and the inspection console is re-open at the first instruction after the current function returned.
  `mkbp(file, line)`
    Command used to emulate method `setbreak` in the inspection console.
  `rmbp(file, line)`
    Command used to emulate method `removebreak` in the inspection console.
  `lsbp()`
    Command used to list all the break point marks currently registered.

### Meta-Fields

`__index = function`
  Retrieves the values of the variables and upvalues accessible through the current inspected scope.
`__newindex = function`
  Changes the values of the variables and upvalues accessible through the current inspected scope.

## Remarks

- All names of fields and methods provided by the `Inspector` instance are always binded to their respective values and therefore hide any variables or upvalues with those names. The same is true for the names of the commands provide by the command prompt that are listed in the description of method `stop`. To access variables or upvalues with those names use commands `loc(`name [, value]`)`, `upv(`name [, value]`)`, `env(`name [, value]`)` and `lua(`name [, value]`)` to access respectively the value of local variables, upvalues global variables of the function's environment, and global variables of the Lua default global environment (i.e. `_G`).

## Examples

### Dining philosophers problem analyzer

    inspector = require("loop.debug.Inspector")()

    -- TIPS:
    -- To check the state of all philosophers, type:
    --   io.write("Forks = ") see(forks)
    --   goto(P1) print("--> P1:") loc()
    --   goto(P2) print("--> P2:") loc()
    --   goto(P3) print("--> P3:") loc()
    --
    -- To remove deadlock condition, type:
    --   forks = {true,true,true}
    --   goto(P1) left,right = false,false
    --   goto(P2) left,right = false,false
    --   goto(P3) left,right = false,false
    --
    -- To avoid that philosophers die from starvation, type:
    --   goto(P1) hunger = 0 goto(P2) hunger = 0 goto(P3) hunger = 0

    local forks = { true, true, true }

    function philo(lfork, rfork)
      local hunger = 0
      local left = false
      local right = false
      repeat
        if hunger == 0 then
          for i=1, math.random(5) do
            coroutine.yield("thinking")
            hunger = hunger + 1
          end
        else
          hunger = hunger + 1
        end
        if not left then
          if forks[lfork]
            then left, forks[lfork] = true, false
            else coroutine.yield("waiting")
          end
        end
        if not right then
          if forks[rfork]
            then right, forks[rfork] = true, false
            else coroutine.yield("waiting")
          end
        end
        if left and right then
          while hunger > 0 do
            coroutine.yield("eating")
            hunger = hunger - 1
          end
          left,  forks[lfork] = false, true
          right, forks[rfork] = false, true
        else
          if hunger > 10 then
            coroutine.yield("starving")
          end
        end
      until hunger > 10
      return "dead"
    end

    P1 = coroutine.create(philo)
    P2 = coroutine.create(philo)
    P3 = coroutine.create(philo)

    local locked = 0
    local failed
    math.randomseed(os.time())
    repeat
      locked = 0
      for i, philo in pairs{ P1, P2, P3 } do
        local ok, msg = coroutine.resume(philo, i, (i+1)%3)
        print("P"..i, msg)
        if ok then
          if msg == "starving" then
            print("STARVATION", "P"..i)
            inspector:stop()
          elseif msg == "waiting" then
            locked = locked + 1
          else
            locked = 0
          end
        else
          failed = true
        end
        if locked >= 3 then
          print("DEADLOCK")
          inspector:stop()
        end
      end
    until failed

Copyright (C) 2004-2008 Tecgraf, PUC-Rio

This project is currently being maintained by Tecgraf at PUC-Rio.