|
| 1 | +using MethodAnalysis |
| 2 | + |
| 3 | +# Analyze MethodInstance signatures and select those that seem at risk for being invalidated. |
| 4 | +function atrisktype(@nospecialize(typ)) |
| 5 | + # signatures like `convert(Vector, a)`, `foo(::Vararg{Synbol,N}) where N` do not seem to pose a problem |
| 6 | + isa(typ, TypeVar) && return false |
| 7 | + # isbits parameters are not a problem |
| 8 | + isa(typ, Type) || return false |
| 9 | + if isa(typ, UnionAll) |
| 10 | + typ = Base.unwrap_unionall(typ) |
| 11 | + end |
| 12 | + # Exclude signatures with Union{} |
| 13 | + typ === Union{} && return false |
| 14 | + isa(typ, Union) && return atrisktype(typ.a) | atrisktype(typ.b) |
| 15 | + # Type{T}: signatures like `convert(::Type{AbstractString}, ::String)` are not problematic, so mark Type as OK |
| 16 | + typ <: Type && return false |
| 17 | + if typ <: Tuple && length(typ.parameters) >= 1 |
| 18 | + p1 = typ.parameters[1] |
| 19 | + # Constructor calls are not themselves a problem (any `convert`s they trigger might be, but those are covered) |
| 20 | + isa(p1, Type) && p1 <: Type && return false |
| 21 | + # convert(::Type{T}, ::S) where S<:T is not problematic |
| 22 | + if p1 === typeof(Base.convert) || p1 === typeof(Core.convert) || p1 === typeof(Core.Compiler.convert) |
| 23 | + p2, p3 = typ.parameters[2], typ.parameters[3] |
| 24 | + if isa(p2, Type) |
| 25 | + p2 = Base.unwrap_unionall(p2) |
| 26 | + if isa(p2, DataType) && length(p2.parameters) === 1 |
| 27 | + T = p2.parameters[1] |
| 28 | + isa(p3, Type) && isa(T, Type) && p3 <: T && return false |
| 29 | + end |
| 30 | + end |
| 31 | + # `getindex`, `length`, etc are OK for various Tuple{T1,T2,...} |
| 32 | + elseif (p1 === typeof(Base.getindex) || p1 === typeof(Core.Compiler.getindex)) || |
| 33 | + (p1 === typeof(Base.length) || p1 === typeof(Core.Compiler.length)) || |
| 34 | + (p1 === typeof(Base.isempty) || p1 === typeof(Core.Compiler.isempty)) || |
| 35 | + (p1 === typeof(Base.iterate) || p1 === typeof(Core.iterate) || p1 === typeof(Core.Compiler.iterate)) |
| 36 | + p2 = typ.parameters[2] |
| 37 | + if isa(p2, Type) |
| 38 | + p2 = Base.unwrap_unionall(p2) |
| 39 | + p2 <: Tuple && return false |
| 40 | + end |
| 41 | + # show(io::IO, x) is OK as long as typeof(x) is safe |
| 42 | + elseif p1 === typeof(Base.show) |
| 43 | + atrisktype(typ.parameters[2]) && return true |
| 44 | + length(typ.parameters) == 3 && atrisktype(typ.parameters[3]) && return true |
| 45 | + return false |
| 46 | + end |
| 47 | + end |
| 48 | + # Standard DataTypes |
| 49 | + isconcretetype(typ) && return false |
| 50 | + # ::Function args are excluded |
| 51 | + typ === Function && return false |
| 52 | + !isempty(typ.parameters) && (any(atrisktype, typ.parameters) || return false) |
| 53 | + return true |
| 54 | +end |
| 55 | + |
| 56 | +# A few tests |
| 57 | +@assert atrisktype(Tuple{typeof(==),Any,Any}) |
| 58 | +@assert atrisktype(Tuple{typeof(==),Symbol,Any}) |
| 59 | +@assert atrisktype(Tuple{typeof(==),Any,Symbol}) |
| 60 | +@assert !atrisktype(Tuple{typeof(==),Symbol,Symbol}) |
| 61 | +@assert !atrisktype(Tuple{typeof(convert),Type{Any},Any}) |
| 62 | +@assert !atrisktype(Tuple{typeof(convert),Type{AbstractString},AbstractString}) |
| 63 | +@assert !atrisktype(Tuple{typeof(convert),Type{AbstractString},String}) |
| 64 | +@assert atrisktype(Tuple{typeof(convert),Type{String},AbstractString}) |
| 65 | +@assert !atrisktype(Tuple{typeof(map),Function,Vector{Any}}) |
| 66 | +@assert !atrisktype(Tuple{typeof(getindex),Dict{Union{String,Int},Any},Union{String,Int}}) |
| 67 | +@assert atrisktype(Tuple{typeof(getindex),Dict{Union{String,Int},Any},Any}) |
| 68 | +@assert !atrisktype(Tuple{Type{BoundsError},Any,Any}) |
| 69 | +@assert atrisktype(Tuple{typeof(sin),Any}) |
| 70 | +@assert !atrisktype(Tuple{typeof(length),Tuple{Any,Any}}) |
| 71 | + |
| 72 | +function collect_mis(m::Method) |
| 73 | + list = Core.MethodInstance[] |
| 74 | + visit(m) do item |
| 75 | + if isa(item, Core.MethodInstance) |
| 76 | + push!(list, item) |
| 77 | + return false |
| 78 | + end |
| 79 | + return true |
| 80 | + end |
| 81 | + return list |
| 82 | +end |
| 83 | + |
| 84 | +const mis = Dict{Method,Vector{Core.MethodInstance}}() |
| 85 | +visit() do item |
| 86 | + if item isa Method |
| 87 | + m = item |
| 88 | + mis[m] = collect_mis(m) |
| 89 | + return false |
| 90 | + end |
| 91 | + return true |
| 92 | +end |
| 93 | + |
| 94 | +# Count # of backedges for MethodInstances with abstract types |
| 95 | +const becounter = Dict{Core.MethodInstance,Int}() |
| 96 | +visit() do item |
| 97 | + if item isa Core.MethodInstance |
| 98 | + if atrisktype(item.specTypes) |
| 99 | + becounter[item] = length(all_backedges(item)) |
| 100 | + end |
| 101 | + return false |
| 102 | + end |
| 103 | + return true |
| 104 | +end |
| 105 | + |
| 106 | +prs = sort!(collect(becounter); by=last) |
| 107 | + |
| 108 | +# Organize them by method instead |
| 109 | + |
| 110 | +const mcounter = Dict{Method,Int}() |
| 111 | +for (mi, c) in becounter |
| 112 | + oc = get(mcounter, mi.def, 0) |
| 113 | + mcounter[mi.def] = oc + c |
| 114 | +end |
| 115 | + |
| 116 | +mprs = sort!(collect(mcounter); by=last) |
| 117 | + |
| 118 | +open("/tmp/methinstdata_$VERSION.log", "w") do io |
| 119 | + for (mi, c) in prs |
| 120 | + c == 0 && continue |
| 121 | + println(io, mi.specTypes=>c) |
| 122 | + end |
| 123 | +end |
| 124 | +open("/tmp/methdata_$VERSION.log", "w") do io |
| 125 | + for (m, c) in mprs |
| 126 | + c == 0 && continue |
| 127 | + println(io, m.sig=>c) |
| 128 | + end |
| 129 | +end |
0 commit comments