在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称:aviatesk/JET.jl开源软件地址:https://github.com/aviatesk/JET.jl开源编程语言:Julia 100.0%开源软件介绍:JET.jlJET.jl employs Julia's type inference to detect potential bugs. !!! note
The latest version of JET requires Julia versions 1.7 and higher;
JET is tested against the current stable release as well as nightly version. !!! tip
Hold on tight and fasten your seatbelt while JET is analyzing your code for the first time.
Once the caches get accumulated, subsequent analysis will run at DemoSay you have this strange and buggy file and want to know where to fix:
# demo
# ====
# fibonacci
# ---------
fib(n) = n ≤ 2 ? n : fib(n-1) + fib(n-2)
fib(1000) # never terminates in ordinal execution
fib(m) # undef var
fib("1000") # obvious type error
# language features
# -----------------
# user-defined types, macros
struct Ty{T}
fld::T
end
function foo(a)
v = Ty(a)
return bar(v)
end
# macros will be expanded
@inline bar(n::T) where {T<:Number} = n < 0 ? zero(T) : one(T)
@inline bar(v::Ty{T}) where {T<:Number} = bar(v.fdl) # typo "fdl"
@inline bar(v::Ty) = bar(convert(Number, v.fld))
foo(1.2)
foo("1") # `String` can't be converted to `Number`
# even staged programming
# adapted from https://github.com/JuliaLang/julia/blob/9f665c19e076ab37cbca2d0cc99283b82e99c26f/base/namedtuple.jl#L253-L264
@generated function badmerge(a::NamedTuple{an}, b::NamedTuple{bn}) where {an, bn}
names = Base.merge_names(an, bn)
types = Base.merge_types(names, a, b)
vals = Any[ :(getfield($(Base.sym_in(names[n], bn) ? :b : :a), $(names[n]))) for n in 1:length(names) ] # missing quote, just ends up with under vars
:( NamedTuple{$names,$types}(($(vals...),)) )
end
badmerge((x=1,y=2), (y=3,z=1)) You can have JET.jl detect possible errors: julia> using JET
julia> report_and_watch_file("demo.jl"; annotate_types = true)
[toplevel-info] virtualized the context of Main (took 0.013 sec)
[toplevel-info] entered into demo.jl
[toplevel-info] exited from demo.jl (took 3.254 sec)
═════ 7 possible errors found ═════
┌ @ demo.jl:10 fib(m)
│ variable m is not defined
└──────────────
┌ @ demo.jl:11 fib("1000")
│┌ @ demo.jl:7 n::String :≤ 2
││┌ @ operators.jl:392 x::String < y::Int64
│││┌ @ operators.jl:343 isless(x::String, y::Int64)
││││ no matching method found for `isless(::String, ::Int64)`: isless(x::String, y::Int64)
│││└────────────────────
┌ @ demo.jl:32 foo(1.2)
│┌ @ demo.jl:24 bar(v::Union{})
││┌ @ demo.jl:29 (v::Ty{Float64}).fdl
│││┌ @ Base.jl:37 Base.getfield(x::Ty{Float64}, f::Symbol)
││││ type Ty{Float64} has no field fdl
│││└──────────────
┌ @ demo.jl:33 foo("1")
│┌ @ demo.jl:24 bar(v::Union{})
││┌ @ demo.jl:30 convert(Number, (v::Ty{String}).fld)
│││ no matching method found for `convert(::Type{Number}, ::String)`: convert(Number, (v::Ty{String}).fld)
││└──────────────
┌ @ demo.jl:44 badmerge(NamedTuple{(:x, :y)}(tuple(1, 2)), NamedTuple{(:y, :z)}(tuple(3, 1)))
│┌ @ demo.jl:37 getfield(a::NamedTuple{(:x, :y), Tuple{Int64, Int64}}, x)
││ variable x is not defined
│└──────────────
│┌ @ demo.jl:37 getfield(b::NamedTuple{(:y, :z), Tuple{Int64, Int64}}, y)
││ variable y is not defined
│└──────────────
│┌ @ demo.jl:37 getfield(b::NamedTuple{(:y, :z), Tuple{Int64, Int64}}, z)
││ variable z is not defined
│└────────────── Hooray!
JET.jl found possible error points (e.g. Note that JET can find these errors while demo.jl is so inefficient (especially the Lastly let's apply the following diff to demo.jl so that it works nicely:
diff --git a/demo.jl b/demo.jl
index f868d2f..634e130 100644
--- a/demo.jl
+++ b/demo.jl
@@ -4,11 +4,21 @@
# fibonacci
# ---------
-fib(n) = n ≤ 2 ? n : fib(n-1) + fib(n-2)
+# cache, cache, cache
+function fib(n::T) where {T<:Number}
+ cache = Dict(zero(T)=>zero(T), one(T)=>one(T))
+ return _fib(n, cache)
+end
+_fib(n, cache) = if haskey(cache, n)
+ cache[n]
+else
+ cache[n] = _fib(n-1, cache) + _fib(n-2, cache)
+end
-fib(1000) # never terminates in ordinal execution
-fib(m) # undef var
-fib("1000") # obvious type error
+fib(BigInt(1000)) # will terminate in ordinal execution as well
+m = 1000 # define m
+fib(m)
+fib(parse(Int, "1000"))
# language features
@@ -26,19 +36,19 @@ end
# macros will be expanded
@inline bar(n::T) where {T<:Number} = n < 0 ? zero(T) : one(T)
-@inline bar(v::Ty{T}) where {T<:Number} = bar(v.fdl) # typo "fdl"
+@inline bar(v::Ty{T}) where {T<:Number} = bar(v.fld) # typo fixed
@inline bar(v::Ty) = bar(convert(Number, v.fld))
foo(1.2)
-foo("1") # `String` can't be converted to `Number`
+foo('1') # `Char` will be converted to `UInt32`
# even staged programming
# adapted from https://github.com/JuliaLang/julia/blob/9f665c19e076ab37cbca2d0cc99283b82e99c26f/base/namedtuple.jl#L253-L264
-@generated function badmerge(a::NamedTuple{an}, b::NamedTuple{bn}) where {an, bn}
+@generated function goodmerge(a::NamedTuple{an}, b::NamedTuple{bn}) where {an, bn}
names = Base.merge_names(an, bn)
types = Base.merge_types(names, a, b)
- vals = Any[ :(getfield($(Base.sym_in(names[n], bn) ? :b : :a), $(names[n]))) for n in 1:length(names) ] # missing quote, just ends up with under vars
+ vals = Any[ :(getfield($(Base.sym_in(names[n], bn) ? :b : :a), $(QuoteNode(names[n])))) for n in 1:length(names) ] # names quoted, should work as expected
:( NamedTuple{$names,$types}(($(vals...),)) )
end
-badmerge((x=1,y=2), (y=3,z=1))
+goodmerge((x=1,y=2), (y=3,z=1)) If you apply the diff (i.e. update and save the demo.jl), JET will automatically re-trigger analysis, and this time, won't complain anything:
[toplevel-info] virtualized the context of Main (took 0.004 sec)
[toplevel-info] entered into demo.jl
[toplevel-info] exited from demo.jl (took 1.375 sec)
No errors detected LimitationsJET explores the functions you call directly as well as their inferrable callees. However, if the argument-types for a call cannot be inferred, JET does not analyze the callee. Consequently, a report of JET integrates with SnoopCompile, and you can sometimes use SnoopCompile to collect the data to perform more comprehensive analyses. SnoopCompile's limitation is that it only collects data for calls that have not been previously inferred, so you must perform this type of analysis in a fresh session. See SnoopCompile's JET-integration documentation for further details. Roadmap
AcknowledgementThis project started as my undergrad thesis project at Kyoto University, supervised by Prof. Takashi Sakuragawa. We were heavily inspired by ruby/typeprof, an experimental type understanding/checking tool for Ruby. The grad thesis about this project is published at https://github.com/aviatesk/grad-thesis, but currently it's only available in Japanese. |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论