# Internals

## Basic usage

The process of executing code in the interpreter is to prepare a `frame` and then
evaluate these statements one-by-one, branching via the `goto` statements as appropriate.
Using the `summer` example described in [Lowered representation](@ref),
let's build a frame:

```julia-repl
julia> frame = JuliaInterpreter.enter_call(summer, A)
Frame for summer(A::AbstractArray{T,N} where N) where T in Main at REPL[2]:2
   1* 2  1 ─       s = (zero)($(Expr(:static_parameter, 1)))
   2  3  │   %2  = A
   3  3  │         #temp# = (iterate)(%2)
⋮
A = [1, 2, 5]
T = Int64
```

This is a [`Frame`](@ref). Only a portion of the `CodeInfo` is shown, a small region surrounding
the current statement (marked with `*` or in yellow text). The full `CodeInfo` can be extracted
as `code = frame.framecode.src`. (It's a slightly modified form of one returned by `@code_lowered`,
in that it has been processed by [`JuliaInterpreter.optimize!`](@ref) to speed up run-time execution.)

`frame` has another field, `framedata`, that holds values needed for or generated by execution.
The input arguments and local variables are in `locals`:

```julia-repl
julia> frame.framedata.locals
5-element Vector{Union{Nothing, Some{Any}}}:
 Some(summer)
 Some([1, 2, 5])
 nothing
 nothing
 nothing
```

These correspond to the `code.slotnames`; the first is the `#self#` argument and the second
is the input array. The remaining local variables (e.g., `s` and `a`), have not yet been assigned---we've
only built the frame, but we haven't yet begun to execute it.
The static parameter, `T`, is stored in `frame.framedata.sparams`:

```julia-repl
julia> frame.framedata.sparams
1-element Vector{Any}:
 Int64
```

The `Expr(:static_parameter, 1)` statement refers to this value.

The other main storage is for the generated SSA values:

```julia-repl
julia> frame.framedata.ssavalues
16-element Vector{Any}:
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
```

Since we haven't executed any statements yet, these are all undefined.

The other main entity is the so-called [program counter](https://en.wikipedia.org/wiki/Program_counter),
which just indicates the next statement to be executed:

```julia-repl
julia> frame.pc
1
```

Let's try executing the first statement:

```julia-repl
julia> JuliaInterpreter.step_expr!(frame)
2
```

This indicates that it ran statement 1 and is prepared to run statement 2.
(It's worth noting that the first line included a `call` to `zero`, so behind the scenes
JuliaInterpreter created a new frame for `zero`, executed all the statements, and then popped
back to `frame`.)
Since the first statement is an assignment of a local variable, let's check the
locals again:

```julia-repl
julia> frame.framedata.locals
5-element Vector{Union{Nothing, Some{Any}}}:
 Some(summer)
 Some([1, 2, 5])
 Some(0)
 nothing
 nothing
```

You can see that the entry corresponding to `s` has been initialized.

The next statement just retrieves one of the slots (the input argument `A`) and stores
it in an SSA value:

```julia-repl
julia> JuliaInterpreter.step_expr!(frame)
3

julia> frame.framedata.ssavalues
16-element Vector{Any}:
 #undef
    [1, 2, 5]
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
 #undef
```

One can easily continue this until execution completes, which is indicated when `step_expr!`
returns `nothing`. Alternatively, use the higher-level `JuliaInterpreter.finish!(frame)`
to step through the entire frame,
or `JuliaInterpreter.finish_and_return!(frame)` to also obtain the return value.

## More complex expressions

Sometimes you might have a whole sequence of expressions you want to run.
In such cases, your first thought should be to construct the `Frame` manually.
Here's a demonstration:

```@setup internals
using JuliaInterpreter;
JuliaInterpreter.clear_caches()
```

```@repl internals
using Test

ex = quote
    x, y = 1, 2
    @test x + y == 3
end;

frame = Frame(Main, ex);

JuliaInterpreter.finish_and_return!(frame)
```

## Toplevel code and world age

Code that defines new `struct`s, new methods, or new modules requires special handling.
Calling `finish_and_return!` on a frame that defines and then calls new objects can trigger a
[world age error](https://docs.julialang.org/en/v1/manual/methods/#Redefining-Methods-1),
in which a newly defined method is considered too new to be called by currently running code.

Here's a demonstration of the problem:

```julia-repl
julia> ex = :(map(x->x^2, [1, 2, 3]));

julia> frame = Frame(Main, ex);

julia> JuliaInterpreter.finish_and_return!(frame)
ERROR: this frame needs to be run at top level
```

The reason for this error becomes clearer if we look directly at the lowered code:

```@setup world-age-example
ex = :(map(x->x^2, [1, 2, 3]))
```

```@repl world-age-example
Meta.lower(Main, ex)
```

The code first defines the anonymous function `x->x^2` (creating an anonymous type and a call
method for it) and then calls `map`. The problem is that the newly defined call method is in a
world newer than the one in which `finish_and_return!` is running.

This can be fixed by indicating that we want to run this frame at top level:

```julia-repl
julia> JuliaInterpreter.finish_and_return!(frame, true)
3-element Vector{Int64}:
 1
 4
 9
```

### Interpreting multi-statement programs

When interpreting code with multiple statements — such as the contents of a source file —
later statements often call methods or use types defined by earlier ones.
`Frame` handles this automatically for `:toplevel` expressions (the representation used
when parsing multiple statements) and `:module` expressions:

```@setup toplevel-example
using JuliaInterpreter
JuliaInterpreter.clear_caches()
```

```@repl toplevel-example
ex = Meta.parseall("""
    f_new(x) = 2x
    g_new() = f_new(21)
    g_new()
""");

ex.head

frame = Frame(Main, ex);

JuliaInterpreter.finish_and_return!(frame)
```

`Meta.parseall` (as opposed to `Meta.parse`) parses the entire string as a sequence of
top-level statements, returning a `:toplevel` expression.
`Frame` recognizes `:toplevel` and `:module` expressions and creates a driver frame that
steps through the unlowered surface statements one at a time, raising the world age between
each so that each statement sees all definitions from earlier ones.

`ExprSplitter` remains available for applications that need fine-grained control
over when each sub-expression is evaluated; `Revise.jl` uses it for this purpose.

(Incidentally, `JuliaInterpreter.enter_call(map, x->x^2, [1, 2, 3])` works fine on its own,
because the anonymous function is defined by the caller — you'll see that the created frame
is very simple.)

### World-age threading

Each `Frame` captures the current world age at construction time. For method frames,
this world is held fixed throughout execution, so stepping sees a consistent view of the
method table even if new methods are defined mid-session. Toplevel driver frames refresh
the world before each statement so they always see the latest definitions.
