reorganize module
This commit is contained in:
		@@ -1,54 +0,0 @@
 | 
			
		||||
Break-Points
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
A _break-point_ **always** stops the current evaluation and calls the [debugging][debugger]
 | 
			
		||||
callback.
 | 
			
		||||
 | 
			
		||||
A break-point is represented by the `debugger::BreakPoint` type, which is an `enum` with
 | 
			
		||||
the following variants.
 | 
			
		||||
 | 
			
		||||
| `BreakPoint` variant                     | Not available under | Description                                                                                                                                 |
 | 
			
		||||
| ---------------------------------------- | :-----------------: | ------------------------------------------------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `AtPosition { source, pos, enabled }`    |   [`no_position`]   | breaks at the specified position in the specified source (empty if none);<br/>if `pos` is at beginning of line, breaks anywhere on the line |
 | 
			
		||||
| `AtFunctionName { name, enabled }`       |                     | breaks when a function matching the specified name is called (can be [operator])                                                            |
 | 
			
		||||
| `AtFunctionCall { name, args, enabled }` |                     | breaks when a function matching the specified name (can be [operator]) and the specified number of arguments is called                      |
 | 
			
		||||
| `AtProperty { name, enabled }`           |    [`no_object`]    | breaks at the specified property access                                                                                                     |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Access Break-Points
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
The following [`debugger::Debugger`] methods allow access to break-points for manipulation.
 | 
			
		||||
 | 
			
		||||
| Method             |      Return type       | Description                                       |
 | 
			
		||||
| ------------------ | :--------------------: | ------------------------------------------------- |
 | 
			
		||||
| `break_points`     |    `&[BreakPoint]`     | returns a slice of all `BreakPoint`'s             |
 | 
			
		||||
| `break_points_mut` | `&mut Vec<BreakPoint>` | returns a mutable reference to all `BreakPoint`'s |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Example
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::debugger::*;
 | 
			
		||||
 | 
			
		||||
let debugger = &mut context.global_runtime_state_mut().debugger_mut();
 | 
			
		||||
 | 
			
		||||
// Get number of break-points.
 | 
			
		||||
let num_break_points = debugger.break_points().len();
 | 
			
		||||
 | 
			
		||||
// Add a new break-point on calls to 'foo(_, _, _)'
 | 
			
		||||
debugger.break_points_mut().push(
 | 
			
		||||
    BreakPoint::AtFunctionCall { name: "foo".into(), args: 3 }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// Display all break-points
 | 
			
		||||
for bp in debugger.break_points().iter() {
 | 
			
		||||
    println!("{bp}");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clear all break-points
 | 
			
		||||
debugger.break_points_mut().clear();
 | 
			
		||||
```
 | 
			
		||||
@@ -1,32 +0,0 @@
 | 
			
		||||
Call Stack
 | 
			
		||||
==========
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
```admonish info.side.wide "Call stack frames"
 | 
			
		||||
 | 
			
		||||
Each "frame" in the call stack corresponds to one layer of [function] call (script-defined
 | 
			
		||||
or native Rust).
 | 
			
		||||
 | 
			
		||||
A call stack frame has the type `debugger::CallStackFrame`.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The [debugger] keeps a _call stack_ of [function] calls with argument values.
 | 
			
		||||
 | 
			
		||||
This call stack can be examined to determine the control flow at any particular point.
 | 
			
		||||
 | 
			
		||||
The `Debugger::call_stack` method returns a slice of all call stack frames.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::debugger::*;
 | 
			
		||||
 | 
			
		||||
let debugger = &mut context.global_runtime_state().debugger();
 | 
			
		||||
 | 
			
		||||
// Get depth of the call stack.
 | 
			
		||||
let depth = debugger.call_stack().len();
 | 
			
		||||
 | 
			
		||||
// Display all function calls
 | 
			
		||||
for frame in debugger.call_stack().iter() {
 | 
			
		||||
    println!("{frame}");
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,110 +0,0 @@
 | 
			
		||||
Register with the Debugger
 | 
			
		||||
==========================
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
Hooking up a debugging interface is as simple as providing closures to the [`Engine`]'s built-in
 | 
			
		||||
debugger via `Engine::register_debugger`.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::debugger::{ASTNode, DebuggerCommand};
 | 
			
		||||
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
engine.register_debugger(
 | 
			
		||||
    // Provide a callback to initialize the debugger state
 | 
			
		||||
    |engine, mut debugger| {
 | 
			
		||||
        debugger.set_state(...);
 | 
			
		||||
        debugger
 | 
			
		||||
    },
 | 
			
		||||
    // Provide a callback for each debugging step
 | 
			
		||||
    |context, event, node, source, pos| {
 | 
			
		||||
        ...
 | 
			
		||||
 | 
			
		||||
        DebuggerCommand::StepOver
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish tip.small "Tip: Accessing the `Debugger`"
 | 
			
		||||
 | 
			
		||||
The type `debugger::Debugger` allows for manipulating [break-points], among others.
 | 
			
		||||
 | 
			
		||||
The [`Engine`]'s debugger instance can be accessed via `context.global_runtime_state().debugger()` (immutable)
 | 
			
		||||
or `context.global_runtime_state_mut().debugger_mut()` (mutable).
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Callback Functions Signature
 | 
			
		||||
----------------------------
 | 
			
		||||
 | 
			
		||||
There are two callback functions to register for the debugger.
 | 
			
		||||
 | 
			
		||||
The first is simply a function to initialize the state of the debugger with the following signature.
 | 
			
		||||
 | 
			
		||||
> ```rust
 | 
			
		||||
> Fn(&Engine, debugger::Debugger) -> debugger::Debugger
 | 
			
		||||
> ```
 | 
			
		||||
 | 
			
		||||
The second callback is a function which will be called by the debugger during each step, with the
 | 
			
		||||
following signature.
 | 
			
		||||
 | 
			
		||||
> ```rust
 | 
			
		||||
> Fn(context: EvalContext, event: debugger::DebuggerEvent, node: ASTNode, source: &str, pos: Position) -> Result<debugger::DebuggerCommand, Box<EvalAltResult>>
 | 
			
		||||
> ```
 | 
			
		||||
 | 
			
		||||
where:
 | 
			
		||||
 | 
			
		||||
| Parameter |      Type       | Description                                                                                                                  |
 | 
			
		||||
| --------- | :-------------: | ---------------------------------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `context` | [`EvalContext`] | the current _evaluation context_                                                                                             |
 | 
			
		||||
| `event`   | `DebuggerEvent` | an `enum` indicating the event that triggered the debugger                                                                   |
 | 
			
		||||
| `node`    |    `ASTNode`    | an `enum` with two variants: `Expr` or `Stmt`, corresponding to the current expression node or statement node in the [`AST`] |
 | 
			
		||||
| `source`  |     `&str`      | the source of the current [`AST`], or empty if none                                                                          |
 | 
			
		||||
| `pos`     |   `Position`    | position of the current node, same as `node.position()`                                                                      |
 | 
			
		||||
 | 
			
		||||
and [`EvalContext`] is a type that encapsulates the current _evaluation context_.
 | 
			
		||||
 | 
			
		||||
### Event
 | 
			
		||||
 | 
			
		||||
The `event` parameter of the second closure passed to `Engine::register_debugger` contains a
 | 
			
		||||
`debugger::DebuggerEvent` which is an `enum` with the following variants.
 | 
			
		||||
 | 
			
		||||
| `DebuggerEvent` variant          | Description                                                                                     |
 | 
			
		||||
| -------------------------------- | ----------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `Start`                          | the debugger is triggered at the beginning of evaluation                                        |
 | 
			
		||||
| `Step`                           | the debugger is triggered at the next step of evaluation                                        |
 | 
			
		||||
| `BreakPoint(`_n_`)`              | the debugger is triggered by the _n_-th [break-point]                                           |
 | 
			
		||||
| `FunctionExitWithValue(`_r_`)`   | the debugger is triggered by a function call returning with value _r_ which is `&Dynamic`       |
 | 
			
		||||
| `FunctionExitWithError(`_err_`)` | the debugger is triggered by a function call exiting with error _err_ which is `&EvalAltResult` |
 | 
			
		||||
| `End`                            | the debugger is triggered at the end of evaluation                                              |
 | 
			
		||||
 | 
			
		||||
### Return value
 | 
			
		||||
 | 
			
		||||
```admonish tip.side.wide "Tip: Initialization"
 | 
			
		||||
 | 
			
		||||
When a script starts evaluation, the debugger always stops at the very _first_ [`AST`] node
 | 
			
		||||
with the `event` parameter set to `DebuggerStatus::Start`.
 | 
			
		||||
 | 
			
		||||
This allows initialization to be done (e.g. setting up [break-points]).
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The second closure passed to `Engine::register_debugger` will be called when stepping into or over
 | 
			
		||||
expressions and statements, or when [break-points] are hit.
 | 
			
		||||
 | 
			
		||||
The return type of the closure is `Result<debugger::DebuggerCommand, Box<EvalAltResult>>`.
 | 
			
		||||
 | 
			
		||||
If an error is returned, the script evaluation at that particular instance returns with that
 | 
			
		||||
particular error. It is thus possible to _abort_ the script evaluation by returning an error that is
 | 
			
		||||
not _catchable_, such as `EvalAltResult::ErrorTerminated`.
 | 
			
		||||
 | 
			
		||||
If no error is returned, then the return `debugger::DebuggerCommand` variant determines the
 | 
			
		||||
continued behavior of the debugger.
 | 
			
		||||
 | 
			
		||||
| `DebuggerCommand` variant | Behavior                                                                                                                        | `gdb` equivalent |
 | 
			
		||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | :--------------: |
 | 
			
		||||
| `Continue`                | continue with normal script evaluation                                                                                          |    `continue`    |
 | 
			
		||||
| `StepInto`                | run to the next expression or statement, diving into functions                                                                  |      `step`      |
 | 
			
		||||
| `StepOver`                | run to the next expression or statement, skipping over functions                                                                |                  |
 | 
			
		||||
| `Next`                    | run to the next statement, skipping over functions                                                                              |      `next`      |
 | 
			
		||||
| `FunctionExit`            | run to the end of the current function call; debugger is triggered _before_ the function call returns and the [`Scope`] cleared |     `finish`     |
 | 
			
		||||
@@ -1,50 +0,0 @@
 | 
			
		||||
Debugging Interface
 | 
			
		||||
===================
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
For systems open to external user-created scripts, it is usually desirable to provide a _debugging_
 | 
			
		||||
experience to the user. The alternative is to provide a custom implementation of [`debug`] via
 | 
			
		||||
`Engine::on_debug` that traps debug output to show in a side panel, for example, which is actually
 | 
			
		||||
extremely simple.
 | 
			
		||||
 | 
			
		||||
Nevertheless, in some systems, it may not be convenient, or even possible, for the user to debug his
 | 
			
		||||
or her scripts simply via good-old [`print`] or [`debug`] statements – the system does not
 | 
			
		||||
have any facility for printed output, for instance.
 | 
			
		||||
 | 
			
		||||
Or the system may require more advanced debugging facilities than mere [`print`] statements –
 | 
			
		||||
such as [break-points].
 | 
			
		||||
 | 
			
		||||
For these advanced scenarios, Rhai contains a _Debugging_ interface, turned on via the [`debugging`]
 | 
			
		||||
feature (which implies the [`internals`] feature).
 | 
			
		||||
 | 
			
		||||
The debugging interface resides under the `debugger` sub-module.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "The Rhai Debugger"
 | 
			
		||||
 | 
			
		||||
The [`rhai-dbg`]({{repoHome}}/src/bin/rhai-dbg.rs) bin tool shows a simple example of
 | 
			
		||||
employing the debugging interface to create a debugger for Rhai scripts!
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Built-in Functions
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
The following functions (defined in the [`DebuggingPackage`][built-in packages] but excluded when
 | 
			
		||||
using a [raw `Engine`]) provides runtime information for debugging purposes.
 | 
			
		||||
 | 
			
		||||
| Function     | Parameter(s) |      Not available under      | Description                                                                                                                                                 |
 | 
			
		||||
| ------------ | ------------ | :---------------------------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `back_trace` | _none_       | [`no_function`], [`no_index`] | returns an [array] of [object maps] or [strings], each containing one level of [function] call;</br>returns an empty [array] if no [debugger] is registered |
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// This recursive function prints its own call stack during each run
 | 
			
		||||
fn foo(x) {
 | 
			
		||||
    print(back_trace());        // prints the current call stack
 | 
			
		||||
 | 
			
		||||
    if x > 0 {
 | 
			
		||||
        foo(x - 1)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,92 +0,0 @@
 | 
			
		||||
Implement a Debugging Server
 | 
			
		||||
============================
 | 
			
		||||
 | 
			
		||||
Sometimes it is desirable to embed a debugging _server_ inside the application such that an external
 | 
			
		||||
debugger interface can connect to the application's running instance at runtime.
 | 
			
		||||
 | 
			
		||||
This way, when scripts are run within the application, it is easy for an external interface to debug
 | 
			
		||||
those scripts as they run.
 | 
			
		||||
 | 
			
		||||
Such connections may take the form of any communication channel, for example a TCP/IP connection, a
 | 
			
		||||
named pipe, or an MPSC channel.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Example
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
### Server side
 | 
			
		||||
 | 
			
		||||
The following example assumes bi-direction, blocking messaging channels, such as a WebSocket
 | 
			
		||||
connection, with a server that accepts connections and creates those channels.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::debugger::{ASTNode, DebuggerCommand};
 | 
			
		||||
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
engine.register_debugger(
 | 
			
		||||
    // Use the initialization callback to set up the communications channel
 | 
			
		||||
    // and listen to it
 | 
			
		||||
    |engine, mut debugger| {
 | 
			
		||||
        // Create server that will listen to requests
 | 
			
		||||
        let mut server = MyCommServer::new();
 | 
			
		||||
        server.listen("localhost:8080");
 | 
			
		||||
 | 
			
		||||
        // Wrap it up in a shared locked cell so it can be 'Clone'
 | 
			
		||||
        let server = Rc::new(RefCell::new(server));
 | 
			
		||||
 | 
			
		||||
        // Store the channel in the debugger state
 | 
			
		||||
        debugger.set_state(Dynamic::from(server));
 | 
			
		||||
        debugger
 | 
			
		||||
    },
 | 
			
		||||
    // Trigger the server during each debugger stop point
 | 
			
		||||
    |context, event, node, source, pos| {
 | 
			
		||||
        // Get the state
 | 
			
		||||
        let mut state = context.tag_mut();
 | 
			
		||||
 | 
			
		||||
        // Get the server
 | 
			
		||||
        let mut server = state.write_lock::<MyCommServer>().unwrap();
 | 
			
		||||
 | 
			
		||||
        // Send the event to the server - blocking call
 | 
			
		||||
        server.send_message(...);
 | 
			
		||||
 | 
			
		||||
        // Receive command - blocking call
 | 
			
		||||
        match server.receive_message() {
 | 
			
		||||
            None => DebuggerCommand::StepOver,
 | 
			
		||||
            // Decode command
 | 
			
		||||
            Ok(...) => { ... }
 | 
			
		||||
            Ok(...) => { ... }
 | 
			
		||||
            Ok(...) => { ... }
 | 
			
		||||
            Ok(...) => { ... }
 | 
			
		||||
            Ok(...) => { ... }
 | 
			
		||||
                :
 | 
			
		||||
                :
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Client side
 | 
			
		||||
 | 
			
		||||
The client can be any system that can work with WebSockets for messaging.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
// Connect to the application's debugger
 | 
			
		||||
let webSocket = new WebSocket("wss://localhost:8080");
 | 
			
		||||
 | 
			
		||||
webSocket.on_message = (event) => {
 | 
			
		||||
    let msg = JSON.parse(event.data);
 | 
			
		||||
 | 
			
		||||
    switch msg.type {
 | 
			
		||||
        // handle debugging events from the application...
 | 
			
		||||
        case "step": {
 | 
			
		||||
                :
 | 
			
		||||
        }
 | 
			
		||||
                :
 | 
			
		||||
                :
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Send command to the application
 | 
			
		||||
webSocket.send("step-over");
 | 
			
		||||
```
 | 
			
		||||
@@ -1,67 +0,0 @@
 | 
			
		||||
Debugger State
 | 
			
		||||
==============
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
Sometimes it is useful to keep a persistent _state_ within the [debugger].
 | 
			
		||||
 | 
			
		||||
The `Engine::register_debugger` API accepts a function that returns the initial value of the
 | 
			
		||||
[debugger's][debugger] state, which is a [`Dynamic`] and can hold any value.
 | 
			
		||||
 | 
			
		||||
This state value is the stored into the [debugger]'s custom state.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Access the Debugger State
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
Use `EvalContext::global_runtime_state().debugger()` (immutable) or
 | 
			
		||||
`EvalContext::global_runtime_state_mut().debugger_mut()` (mutable) to gain access to the current
 | 
			
		||||
[`debugger::Debugger`] instance.
 | 
			
		||||
 | 
			
		||||
The following [`debugger::Debugger`] methods allow access to the custom [debugger] state.
 | 
			
		||||
 | 
			
		||||
| Method      |          Parameter type           |         Return type         | Description                                     |
 | 
			
		||||
| ----------- | :-------------------------------: | :-------------------------: | ----------------------------------------------- |
 | 
			
		||||
| `state`     |              _none_               |   [`&Dynamic`][`Dynamic`]   | returns the custom state                        |
 | 
			
		||||
| `state_mut` |              _none_               | [`&mut Dynamic`][`Dynamic`] | returns a mutable reference to the custom state |
 | 
			
		||||
| `set_state` | [`impl Into<Dynamic>`][`Dynamic`] |           _none_            | sets the value of the custom state              |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Example
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
engine.register_debugger(
 | 
			
		||||
    |engine, mut debugger| {
 | 
			
		||||
        // Say, use an object map for the debugger state
 | 
			
		||||
        let mut state = Map::new();
 | 
			
		||||
        // Initialize properties
 | 
			
		||||
        state.insert("hello".into(), 42_64.into());
 | 
			
		||||
        state.insert("foo".into(), false.into());
 | 
			
		||||
        
 | 
			
		||||
        debugger.set_state(state);
 | 
			
		||||
        debugger
 | 
			
		||||
    },
 | 
			
		||||
    |context, node, source, pos| {
 | 
			
		||||
        // Print debugger state - which is an object map
 | 
			
		||||
        let state = context.global_runtime_state().debugger().state();
 | 
			
		||||
        println!("Current state = {state}");
 | 
			
		||||
 | 
			
		||||
        // Get the state as an object map
 | 
			
		||||
        let mut state = context.global_runtime_state_mut()
 | 
			
		||||
                               .debugger_mut().state_mut()
 | 
			
		||||
                               .write_lock::<Map>().unwrap();
 | 
			
		||||
 | 
			
		||||
        // Read state
 | 
			
		||||
        let hello = state.get("hello").unwrap().as_int().unwrap();
 | 
			
		||||
 | 
			
		||||
        // Modify state
 | 
			
		||||
        state.insert("hello".into(), (hello + 1).into());
 | 
			
		||||
        state.insert("foo".into(), true.into());
 | 
			
		||||
        state.insert("something_new".into(), "hello, world!".into());
 | 
			
		||||
 | 
			
		||||
        // Continue with debugging
 | 
			
		||||
        Ok(DebuggerCommand::StepInto)
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
		Reference in New Issue
	
	Block a user