Skip to content

Commit a3ed4cc

Browse files
aviateskserenity4
andauthored
change the recurse interface to AbstractInterpreter-like interface (#683)
Align the `recurse` argument to something like the base Compiler's `AbstractInterpreter` and make JuliaInterpreter routines overloadable properly. This change is quite breaking (thus bumping the minor version of this package), but necessary to enhance the customizability of JI. For example, it will make it easier to add changes like #682 in a nicer way, but also should enable better designs in packages such as Revise and JET. - allow specifying interpreter with `@interpret` - better error handling for `@interpret` - better `native_call` implementation - make `lookup` overloadable - define `NonRecursiveInterpreter` and make `Compiled` alias to it `Compiled` is preserved for backward compatibility. --------- Co-authored-by: Cédric Belmant <[email protected]>
1 parent 43f0215 commit a3ed4cc

15 files changed

+622
-538
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "JuliaInterpreter"
22
uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a"
3-
version = "0.9.46"
3+
version = "0.10.0"
44

55
[deps]
66
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"

bin/generate_builtins.jl

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const REQUIRES_WORLD = Core.Builtin[
4141
replaceglobal!,
4242
setglobalonce!,
4343
]
44-
const CALL_LATEST = """args = getargs(args, frame)
44+
const CALL_LATEST = """args = getargs(interp, args, frame)
4545
if !expand
4646
return Some{Any}(Core._call_latest(args...))
4747
end
@@ -50,7 +50,7 @@ const CALL_LATEST = """args = getargs(args, frame)
5050
for x in args
5151
push!(new_expr.args, QuoteNode(x))
5252
end
53-
return maybe_recurse_expanded_builtin(frame, new_expr)
53+
return maybe_recurse_expanded_builtin(interp, frame, new_expr)
5454
"""
5555

5656
function scopedname(f)
@@ -90,7 +90,7 @@ function generate_fcall_nargs(fname, minarg, maxarg; requires_world::Bool=false)
9090
wrapper *= "$nargs\n "
9191
argcall = ""
9292
for i = 1:nargs
93-
argcall *= "lookup(frame, args[$(i+1)])"
93+
argcall *= "lookup(interp, frame, args[$(i+1)])"
9494
if i < nargs
9595
argcall *= ", "
9696
end
@@ -104,9 +104,9 @@ function generate_fcall_nargs(fname, minarg, maxarg; requires_world::Bool=false)
104104
wrapper *= "\n else"
105105
# To throw the correct error
106106
if requires_world
107-
wrapper *= "\n return Some{Any}(Base.invoke_in_world(frame.world, $fname, getargs(args, frame)...)$annotation)"
107+
wrapper *= "\n return Some{Any}(Base.invoke_in_world(frame.world, $fname, getargs(interp, args, frame)...)$annotation)"
108108
else
109-
wrapper *= "\n return Some{Any}($fname(getargs(args, frame)...)$annotation)"
109+
wrapper *= "\n return Some{Any}($fname(getargs(interp, args, frame)...)$annotation)"
110110
end
111111
wrapper *= "\n end"
112112
return wrapper
@@ -122,9 +122,9 @@ function generate_fcall(f, table, id)
122122
# A built-in with arbitrary or unknown number of arguments.
123123
# This will (unfortunately) use dynamic dispatch.
124124
if requires_world
125-
return "return Some{Any}(Base.invoke_in_world(frame.world, $fname, getargs(args, frame)...))"
125+
return "return Some{Any}(Base.invoke_in_world(frame.world, $fname, getargs(interp, args, frame)...))"
126126
end
127-
return "return Some{Any}($fname(getargs(args, frame)...))"
127+
return "return Some{Any}($fname(getargs(interp, args, frame)...))"
128128
end
129129

130130
# `io` is for the generated source file
@@ -140,42 +140,42 @@ function generate_builtins(io::IO)
140140
"""
141141
# This file is generated by `generate_builtins.jl`. Do not edit by hand.
142142
143-
function getargs(args, frame)
143+
function getargs(interp::Interpreter, args::Vector{Any}, frame::Frame)
144144
nargs = length(args)-1 # skip f
145145
callargs = resize!(frame.framedata.callargs, nargs)
146146
for i = 1:nargs
147-
callargs[i] = lookup(frame, args[i+1])
147+
callargs[i] = lookup(interp, frame, args[i+1])
148148
end
149149
return callargs
150150
end
151151
152152
const kwinvoke = Core.kwfunc(Core.invoke)
153153
154-
function maybe_recurse_expanded_builtin(frame, new_expr)
154+
function maybe_recurse_expanded_builtin(interp::Interpreter, frame::Frame, new_expr::Expr)
155155
f = new_expr.args[1]
156156
if isa(f, Core.Builtin) || isa(f, Core.IntrinsicFunction)
157-
return maybe_evaluate_builtin(frame, new_expr, true)
157+
return maybe_evaluate_builtin(interp, frame, new_expr, true)
158158
else
159159
return new_expr
160160
end
161161
end
162162
163163
\"\"\"
164-
ret = maybe_evaluate_builtin(frame, call_expr, expand::Bool)
164+
ret = maybe_evaluate_builtin(interp::Interpreter, frame::Frame, call_expr::Expr, expand::Bool)
165165
166166
If `call_expr` is to a builtin function, evaluate it, returning the result inside a `Some` wrapper.
167167
Otherwise, return `call_expr`.
168168
169169
If `expand` is true, `Core._apply_iterate` calls will be resolved as a call to the applied function.
170170
\"\"\"
171-
function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
171+
function maybe_evaluate_builtin(interp::Interpreter, frame::Frame, call_expr::Expr, expand::Bool)
172172
args = call_expr.args
173173
nargs = length(args) - 1
174174
fex = args[1]
175175
if isa(fex, QuoteNode)
176176
f = fex.value
177177
else
178-
f = lookup(frame, fex)
178+
f = lookup(interp, frame, fex)
179179
end
180180
181181
if f isa Core.OpaqueClosure
@@ -208,15 +208,15 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
208208
print(io,
209209
"""
210210
$head f === tuple
211-
return Some{Any}(ntupleany(i::Int->lookup(frame, args[i+1]), length(args)-1))
211+
return Some{Any}(ntupleany(i::Int->lookup(interp, frame, args[i+1]), length(args)-1))
212212
""")
213213
continue
214214
elseif f === Core._apply_iterate
215215
# Resolve varargs calls
216216
print(io,
217217
"""
218218
$head f === Core._apply_iterate
219-
argswrapped = getargs(args, frame)
219+
argswrapped = getargs(interp, args, frame)
220220
if !expand
221221
return Some{Any}(Core._apply_iterate(argswrapped...))
222222
end
@@ -229,7 +229,7 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
229229
for x in argsflat
230230
push!(new_expr.args, QuoteNode(x))
231231
end
232-
return maybe_recurse_expanded_builtin(frame, new_expr)
232+
return maybe_recurse_expanded_builtin(interp, frame, new_expr)
233233
""")
234234
continue
235235
elseif f === Core.invoke
@@ -238,7 +238,7 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
238238
"""
239239
$head f === $fstr
240240
if !expand
241-
argswrapped = getargs(args, frame)
241+
argswrapped = getargs(interp, args, frame)
242242
return Some{Any}($fstr(argswrapped...))
243243
end
244244
# This uses the original arguments to avoid looking them up twice
@@ -257,7 +257,7 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
257257
end
258258
return Some{Any}(currscope)
259259
else
260-
return Some{Any}(Core.current_scope(getargs(args, frame)...))
260+
return Some{Any}(Core.current_scope(getargs(interp, args, frame)...))
261261
end
262262
""")
263263
continue
@@ -293,13 +293,13 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
293293
if nargs == 1
294294
call_expr = copy(call_expr)
295295
args2 = args[2]
296-
call_expr.args[2] = isa(args2, QuoteNode) ? args2 : lookup(frame, args2)
296+
call_expr.args[2] = isa(args2, QuoteNode) ? args2 : lookup(interp, frame, args2)
297297
return Some{Any}(Core.eval(moduleof(frame), call_expr))
298298
elseif nargs == 2
299299
call_expr = copy(call_expr)
300300
args2 = args[2]
301-
call_expr.args[2] = isa(args2, QuoteNode) ? args2 : lookup(frame, args2)
302-
call_expr.args[3] = lookup(frame, args[3])
301+
call_expr.args[2] = isa(args2, QuoteNode) ? args2 : lookup(interp, frame, args2)
302+
call_expr.args[3] = lookup(interp, frame, args[3])
303303
return Some{Any}(Core.eval(moduleof(frame), call_expr))
304304
end
305305
""")
@@ -322,7 +322,7 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
322322
_scopedname = "$mod.$name"
323323
fcall = name === :_call_latest ? CALL_LATEST :
324324
maxarg < typemax(Int) ? generate_fcall_nargs(_scopedname, minarg, maxarg) :
325-
"return Some{Any}($_scopedname(getargs(args, frame)...))"
325+
"return Some{Any}($_scopedname(getargs(interp, args, frame)...))"
326326
rname = repr(name)
327327
print(io,
328328
"""
@@ -361,7 +361,7 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
361361
print(io,
362362
"""
363363
if isa(f, Core.IntrinsicFunction)
364-
cargs = getargs(args, frame)
364+
cargs = getargs(interp, args, frame)
365365
if f === Core.Intrinsics.have_fma && length(cargs) == 1
366366
cargs1 = cargs[1]
367367
if cargs1 == Float64
@@ -392,7 +392,7 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
392392
"""
393393
end
394394
if isa(f, typeof(kwinvoke))
395-
return Some{Any}(kwinvoke(getargs(args, frame)...))
395+
return Some{Any}(kwinvoke(getargs(interp, args, frame)...))
396396
end
397397
return call_expr
398398
end

docs/src/dev_reference.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@
66
@interpret
77
```
88

9+
## Interpreter
10+
11+
```@docs
12+
JuliaInterpreter.Interpreter
13+
JuliaInterpreter.RecursiveInterpreter
14+
JuliaInterpreter.NonRecursiveInterpreter
15+
JuliaInterpreter.Compiled
16+
```
17+
918
## Frame creation
1019

1120
```@docs
@@ -31,11 +40,11 @@ leaf
3140
## Frame execution
3241

3342
```@docs
34-
JuliaInterpreter.Compiled
3543
JuliaInterpreter.step_expr!
3644
JuliaInterpreter.finish!
3745
JuliaInterpreter.finish_and_return!
3846
JuliaInterpreter.finish_stack!
47+
JuliaInterpreter.finish_nested_frame!
3948
JuliaInterpreter.get_return
4049
JuliaInterpreter.next_until!
4150
JuliaInterpreter.maybe_next_until!
@@ -67,7 +76,7 @@ toggle
6776
break_on
6877
break_off
6978
breakpoints
70-
JuliaInterpreter.dummy_breakpoint
79+
JuliaInterpreter.BreakOnCall
7180
```
7281

7382
## Types

0 commit comments

Comments
 (0)