From 10ead4e0544ba0a51c37f48aa21b3c3bc7e1073c Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Thu, 17 Oct 2024 22:12:37 +0200 Subject: [PATCH 01/20] add seal_methodtable --- base/runtime_internals.jl | 19 +++++++++++++++++++ src/gf.c | 13 ++++++++++++- src/jl_exported_funcs.inc | 1 + src/method.c | 2 +- 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 645aa55c538b4..07dccb061d2a4 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -1182,6 +1182,25 @@ function signature_type(@nospecialize(f), @nospecialize(argtypes)) return rewrap_unionall(Tuple{ft, u.parameters...}, argtypes) end +""" + delete_method(m::Method) + +Make method `m` uncallable and force recompilation of any methods that use(d) it. +""" +function delete_method(m::Method) + ccall(:jl_method_table_disable, Cvoid, (Any, Any), get_methodtable(m), m) +end + +""" + seal_methodtable(m::Core.MethodTable) + +Disallow adding or modifyng methods of `mt`. +""" +function seal_methodtable(m::Core.MethodTable) + ccall(:jl_method_table_seal, Cvoid, (Any,), m) +end +seal_methodtable(m::Nothing) = nothing + function get_methodtable(m::Method) mt = ccall(:jl_method_get_table, Any, (Any,), m) if mt === nothing diff --git a/src/gf.c b/src/gf.c index fc2e62ebff96b..8109b1cae91a1 100644 --- a/src/gf.c +++ b/src/gf.c @@ -772,7 +772,7 @@ static int reset_mt_caches(jl_methtable_t *mt, void *env) { // removes all method caches // this might not be entirely safe (GC or MT), thus we only do it very early in bootstrapping - if (!mt->frozen) { // make sure not to reset builtin functions + if (!mt->frozen) { // make sure not to reset frozen functions jl_atomic_store_release(&mt->leafcache, (jl_genericmemory_t*)jl_an_empty_memory_any); jl_atomic_store_release(&mt->cache, jl_nothing); } @@ -2028,6 +2028,17 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho JL_UNLOCK(&world_counter_lock); } +JL_DLLEXPORT void jl_method_table_seal(jl_methtable_t *mt) +{ + JL_LOCK(&world_counter_lock); + JL_LOCK(&mt->writelock); + size_t world = jl_atomic_load_relaxed(&jl_world_counter); + mt->frozen = 1; + jl_atomic_store_release(&jl_world_counter, world + 1); + JL_UNLOCK(&mt->writelock); + JL_UNLOCK(&world_counter_lock); +} + static int jl_type_intersection2(jl_value_t *t1, jl_value_t *t2, jl_value_t **isect JL_REQUIRE_ROOTED_SLOT, jl_value_t **isect2 JL_REQUIRE_ROOTED_SLOT) { *isect2 = NULL; diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index f712f154ed896..32aaf13435d84 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -305,6 +305,7 @@ XX(jl_method_instance_add_backedge) \ XX(jl_method_table_add_backedge) \ XX(jl_method_table_disable) \ + XX(jl_method_table_seal) \ XX(jl_method_table_for) \ XX(jl_method_table_insert) \ XX(jl_methtable_lookup) \ diff --git a/src/method.c b/src/method.c index 629816319b334..22c4bb4706bbf 100644 --- a/src/method.c +++ b/src/method.c @@ -1227,7 +1227,7 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, if ((jl_value_t*)mt == jl_nothing) jl_error("Method dispatch is unimplemented currently for this method signature"); if (mt->frozen) - jl_error("cannot add methods to a builtin function"); + jl_error("cannot add methods to or modify methods of a sealed function"); assert(jl_is_linenode(functionloc)); jl_sym_t *file = (jl_sym_t*)jl_linenode_file(functionloc); From 8bef774a3c5d64041165425096a12d873a002153 Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Sun, 13 Oct 2024 19:27:38 +0200 Subject: [PATCH 02/20] add test --- test/reflection.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/reflection.jl b/test/reflection.jl index 634390e0680d1..47e08542b6d75 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -1298,3 +1298,11 @@ end @test Base.infer_return_type(code_lowered, (Any,Any)) == Vector{Core.CodeInfo} @test methods(Union{}) == Any[m.method for m in Base._methods_by_ftype(Tuple{Core.TypeofBottom, Vararg}, 1, Base.get_world_counter())] # issue #55187 + +# sealing +f_sealed(x::Int64) = x+1 +Base.seal_methodtable(Base.methods(f_sealed).mt) +@test_throws( + ErrorException("cannot add methods to or modify methods of a sealed function"), + @eval f_sealed(x::Float64) = x+2 +) From 22b224ea7c062d3c68c8d4460bcf842a9022fa6b Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Thu, 17 Oct 2024 22:13:47 +0200 Subject: [PATCH 03/20] rm unused method --- base/runtime_internals.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 07dccb061d2a4..97ae69ce1f471 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -1199,7 +1199,6 @@ Disallow adding or modifyng methods of `mt`. function seal_methodtable(m::Core.MethodTable) ccall(:jl_method_table_seal, Cvoid, (Any,), m) end -seal_methodtable(m::Nothing) = nothing function get_methodtable(m::Method) mt = ccall(:jl_method_get_table, Any, (Any,), m) From f39b7005760c9578f4bd872d8a9c5c9785f4e2b8 Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Thu, 17 Oct 2024 22:14:43 +0200 Subject: [PATCH 04/20] Fix typo in docstring Co-authored-by: Alex Arslan --- base/runtime_internals.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 97ae69ce1f471..e690659857b61 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -1194,7 +1194,7 @@ end """ seal_methodtable(m::Core.MethodTable) -Disallow adding or modifyng methods of `mt`. +Disallow adding or modifying methods of `mt`. """ function seal_methodtable(m::Core.MethodTable) ccall(:jl_method_table_seal, Cvoid, (Any,), m) From 6580a915d7bf4f97537133cb887acd1025747bdc Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Thu, 17 Oct 2024 22:16:12 +0200 Subject: [PATCH 05/20] rename seal_methodtable to freeze! --- base/runtime_internals.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index e690659857b61..17b438b373190 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -1192,12 +1192,12 @@ function delete_method(m::Method) end """ - seal_methodtable(m::Core.MethodTable) + freeze!(mt::Core.MethodTable) Disallow adding or modifying methods of `mt`. """ -function seal_methodtable(m::Core.MethodTable) - ccall(:jl_method_table_seal, Cvoid, (Any,), m) +function freeze!(mt::Core.MethodTable) + ccall(:jl_method_table_seal, Cvoid, (Any,), mt) end function get_methodtable(m::Method) From 67830e855c4f486544c1a729d126d8627a92df55 Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Thu, 17 Oct 2024 22:17:12 +0200 Subject: [PATCH 06/20] add unfreeze! as counterpart to freeze! --- base/runtime_internals.jl | 15 ++++++++++++++- src/gf.c | 4 ++-- src/jl_exported_funcs.inc | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 17b438b373190..30ba2def44377 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -1195,9 +1195,22 @@ end freeze!(mt::Core.MethodTable) Disallow adding or modifying methods of `mt`. + +See also [`unfreeze!(mt)`](@ref unfreeze!). """ function freeze!(mt::Core.MethodTable) - ccall(:jl_method_table_seal, Cvoid, (Any,), mt) + ccall(:jl_method_table_set_frozen, Cvoid, (Any,Cint), mt, 1) +end + +""" + unfreeze!(mt::Core.MethodTable) + +Allow adding or modifying methods of `mt`. + +See also [`freeze!(mt)`](@ref freeze!). +""" +function unfreeze!(mt::Core.MethodTable) + ccall(:jl_method_table_set_frozen, Cvoid, (Any,Cint), mt, 0) end function get_methodtable(m::Method) diff --git a/src/gf.c b/src/gf.c index 8109b1cae91a1..5b6cd5f5fa9f3 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2028,12 +2028,12 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho JL_UNLOCK(&world_counter_lock); } -JL_DLLEXPORT void jl_method_table_seal(jl_methtable_t *mt) +JL_DLLEXPORT void jl_method_table_set_frozen(jl_methtable_t *mt, int val) { JL_LOCK(&world_counter_lock); JL_LOCK(&mt->writelock); size_t world = jl_atomic_load_relaxed(&jl_world_counter); - mt->frozen = 1; + mt->frozen = val; jl_atomic_store_release(&jl_world_counter, world + 1); JL_UNLOCK(&mt->writelock); JL_UNLOCK(&world_counter_lock); diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 32aaf13435d84..5ae8b661deb69 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -305,7 +305,7 @@ XX(jl_method_instance_add_backedge) \ XX(jl_method_table_add_backedge) \ XX(jl_method_table_disable) \ - XX(jl_method_table_seal) \ + XX(jl_method_table_set_frozen) \ XX(jl_method_table_for) \ XX(jl_method_table_insert) \ XX(jl_methtable_lookup) \ From c206b09e1a1decee4dee13fbe366c4a42f447f31 Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Thu, 17 Oct 2024 22:18:01 +0200 Subject: [PATCH 07/20] reword error msg --- base/runtime_internals.jl | 4 ++-- src/method.c | 2 +- test/reflection.jl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 30ba2def44377..2f69e42562972 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -1199,7 +1199,7 @@ Disallow adding or modifying methods of `mt`. See also [`unfreeze!(mt)`](@ref unfreeze!). """ function freeze!(mt::Core.MethodTable) - ccall(:jl_method_table_set_frozen, Cvoid, (Any,Cint), mt, 1) + ccall(:jl_method_table_set_frozen, Cvoid, (Any, Cint), mt, 1) end """ @@ -1210,7 +1210,7 @@ Allow adding or modifying methods of `mt`. See also [`freeze!(mt)`](@ref freeze!). """ function unfreeze!(mt::Core.MethodTable) - ccall(:jl_method_table_set_frozen, Cvoid, (Any,Cint), mt, 0) + ccall(:jl_method_table_set_frozen, Cvoid, (Any, Cint), mt, 0) end function get_methodtable(m::Method) diff --git a/src/method.c b/src/method.c index 22c4bb4706bbf..10c7e3a2ddab9 100644 --- a/src/method.c +++ b/src/method.c @@ -1227,7 +1227,7 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, if ((jl_value_t*)mt == jl_nothing) jl_error("Method dispatch is unimplemented currently for this method signature"); if (mt->frozen) - jl_error("cannot add methods to or modify methods of a sealed function"); + jl_error("cannot add methods to or modify methods of a frozen function"); assert(jl_is_linenode(functionloc)); jl_sym_t *file = (jl_sym_t*)jl_linenode_file(functionloc); diff --git a/test/reflection.jl b/test/reflection.jl index 47e08542b6d75..d15ea8cafc456 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -1303,6 +1303,6 @@ end f_sealed(x::Int64) = x+1 Base.seal_methodtable(Base.methods(f_sealed).mt) @test_throws( - ErrorException("cannot add methods to or modify methods of a sealed function"), + ErrorException("cannot add methods to or modify methods of a frozen function"), @eval f_sealed(x::Float64) = x+2 ) From e229f4fb2d347b823fac9ed04b8f03efd15a25de Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 17 Oct 2024 19:13:19 +0200 Subject: [PATCH 08/20] Apply suggestions from code review Co-authored-by: Alex Arslan --- test/reflection.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/reflection.jl b/test/reflection.jl index d15ea8cafc456..6632549457d0d 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -1301,8 +1301,12 @@ end # sealing f_sealed(x::Int64) = x+1 -Base.seal_methodtable(Base.methods(f_sealed).mt) +Base.freeze!(Base.methods(f_sealed).mt) @test_throws( ErrorException("cannot add methods to or modify methods of a frozen function"), @eval f_sealed(x::Float64) = x+2 ) +Base.unfreeze!(Base.methods(f_sealed).mt) +f_sealed(x::Float64) = x + 2 +@test f_sealed(1) == 2 +@test f_sealed(1.0) == 2.0 From 6284eab4780238e485b5aeb8d162f91d79bd5034 Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Thu, 17 Oct 2024 22:02:38 +0200 Subject: [PATCH 09/20] update tests --- test/core.jl | 2 +- test/reflection.jl | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/core.jl b/test/core.jl index 5ba0e99e730d4..1ff4afe99c687 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2650,7 +2650,7 @@ for f in (:Any, :Function, :(Core.Builtin), :(Union{Nothing, Type}), :(Union{typ @test_throws ErrorException("Method dispatch is unimplemented currently for this method signature") @eval (::$f)() = 1 end for f in (:(Core.getfield), :((::typeof(Core.getfield))), :((::Core.IntrinsicFunction))) - @test_throws ErrorException("cannot add methods to a builtin function") @eval $f() = 1 + @test_throws ErrorException("cannot add methods to or modify methods of a frozen function") @eval $f() = 1 end # issue #33370 diff --git a/test/reflection.jl b/test/reflection.jl index 6632549457d0d..9bea2d5dfa58b 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -1299,14 +1299,14 @@ end @test methods(Union{}) == Any[m.method for m in Base._methods_by_ftype(Tuple{Core.TypeofBottom, Vararg}, 1, Base.get_world_counter())] # issue #55187 -# sealing -f_sealed(x::Int64) = x+1 -Base.freeze!(Base.methods(f_sealed).mt) +# freezing Core.MethodTable +f_frozen(x::Int) = x+1 +Base.freeze!(Base.methods(f_frozen).mt) @test_throws( ErrorException("cannot add methods to or modify methods of a frozen function"), - @eval f_sealed(x::Float64) = x+2 + @eval f_frozen(x::Float64) = x+2 ) -Base.unfreeze!(Base.methods(f_sealed).mt) -f_sealed(x::Float64) = x + 2 -@test f_sealed(1) == 2 -@test f_sealed(1.0) == 2.0 +Base.unfreeze!(Base.methods(f_frozen).mt) +f_frozen(x::Float64) = x+2 +@test f_frozen(1) == 2 +@test f_frozen(1.0) == 3.0 From f98bd1d395c0347f26bc628d3171b19b365ab6c3 Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 17 Oct 2024 23:22:05 +0200 Subject: [PATCH 10/20] rm unfreeze! again and rm rebase mistake Co-authored-by: Jameson Nash --- base/runtime_internals.jl | 20 -------------------- test/reflection.jl | 4 ---- 2 files changed, 24 deletions(-) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 2f69e42562972..49e749a19a789 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -1182,15 +1182,6 @@ function signature_type(@nospecialize(f), @nospecialize(argtypes)) return rewrap_unionall(Tuple{ft, u.parameters...}, argtypes) end -""" - delete_method(m::Method) - -Make method `m` uncallable and force recompilation of any methods that use(d) it. -""" -function delete_method(m::Method) - ccall(:jl_method_table_disable, Cvoid, (Any, Any), get_methodtable(m), m) -end - """ freeze!(mt::Core.MethodTable) @@ -1202,17 +1193,6 @@ function freeze!(mt::Core.MethodTable) ccall(:jl_method_table_set_frozen, Cvoid, (Any, Cint), mt, 1) end -""" - unfreeze!(mt::Core.MethodTable) - -Allow adding or modifying methods of `mt`. - -See also [`freeze!(mt)`](@ref freeze!). -""" -function unfreeze!(mt::Core.MethodTable) - ccall(:jl_method_table_set_frozen, Cvoid, (Any, Cint), mt, 0) -end - function get_methodtable(m::Method) mt = ccall(:jl_method_get_table, Any, (Any,), m) if mt === nothing diff --git a/test/reflection.jl b/test/reflection.jl index 9bea2d5dfa58b..5b5bdbba92e41 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -1306,7 +1306,3 @@ Base.freeze!(Base.methods(f_frozen).mt) ErrorException("cannot add methods to or modify methods of a frozen function"), @eval f_frozen(x::Float64) = x+2 ) -Base.unfreeze!(Base.methods(f_frozen).mt) -f_frozen(x::Float64) = x+2 -@test f_frozen(1) == 2 -@test f_frozen(1.0) == 3.0 From a0de0b32c9d850d4a8d530c0b4fcd0731ee189c2 Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 17 Oct 2024 23:36:33 +0200 Subject: [PATCH 11/20] rm locking and world age increments Co-authored-by: Jameson Nash --- src/gf.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/gf.c b/src/gf.c index 5b6cd5f5fa9f3..cdebc53259430 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2030,13 +2030,7 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho JL_DLLEXPORT void jl_method_table_set_frozen(jl_methtable_t *mt, int val) { - JL_LOCK(&world_counter_lock); - JL_LOCK(&mt->writelock); - size_t world = jl_atomic_load_relaxed(&jl_world_counter); mt->frozen = val; - jl_atomic_store_release(&jl_world_counter, world + 1); - JL_UNLOCK(&mt->writelock); - JL_UNLOCK(&world_counter_lock); } static int jl_type_intersection2(jl_value_t *t1, jl_value_t *t2, jl_value_t **isect JL_REQUIRE_ROOTED_SLOT, jl_value_t **isect2 JL_REQUIRE_ROOTED_SLOT) From bb5b4ab0cdb2a348f9f7882947d63c74898b084f Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 17 Oct 2024 23:37:14 +0200 Subject: [PATCH 12/20] use setfield! instead of ccall Co-authored-by: Jameson Nash --- base/runtime_internals.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 49e749a19a789..5c8e141f14e10 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -1190,7 +1190,7 @@ Disallow adding or modifying methods of `mt`. See also [`unfreeze!(mt)`](@ref unfreeze!). """ function freeze!(mt::Core.MethodTable) - ccall(:jl_method_table_set_frozen, Cvoid, (Any, Cint), mt, 1) + setfield!(mt, nfields(mt), 0x1) end function get_methodtable(m::Method) From c08a509e5ca648eff8bf023715a61ea1fdb5bcf8 Mon Sep 17 00:00:00 2001 From: Florian Date: Fri, 18 Oct 2024 00:06:08 +0200 Subject: [PATCH 13/20] morph `freeze!` into `morespecific!` Co-authored-by: Jameson Nash --- base/runtime_internals.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 5c8e141f14e10..ac424df4be577 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -1189,7 +1189,11 @@ Disallow adding or modifying methods of `mt`. See also [`unfreeze!(mt)`](@ref unfreeze!). """ -function freeze!(mt::Core.MethodTable) +function morespecific!(m::Method) + mt = get_methodtable(m) + # check that all Methods in this table are morespecific than m + # so we might avoid disabling a table that might get used for more than just subsets of m + all(m2 -> m === m2 || morespecific(m2, m), MethodList(mt)) || error("unsupported Method to disable") setfield!(mt, nfields(mt), 0x1) end From 88b18a9ee2924c276732df3599e28a64789fbcd3 Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Fri, 18 Oct 2024 00:05:42 +0200 Subject: [PATCH 14/20] update test --- test/reflection.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/reflection.jl b/test/reflection.jl index 5b5bdbba92e41..911e6009e80b5 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -1299,10 +1299,10 @@ end @test methods(Union{}) == Any[m.method for m in Base._methods_by_ftype(Tuple{Core.TypeofBottom, Vararg}, 1, Base.get_world_counter())] # issue #55187 -# freezing Core.MethodTable -f_frozen(x::Int) = x+1 -Base.freeze!(Base.methods(f_frozen).mt) +# disallow adding new methods +f_sealed(x::Int) = x+1 +Base.morespecific!(which(f_frozen, (Int,))) @test_throws( ErrorException("cannot add methods to or modify methods of a frozen function"), - @eval f_frozen(x::Float64) = x+2 + @eval f_sealed(x::Float64) = x+2 ) From e1232bca9db4249ba9eba1f8cbe32083fa354b10 Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Fri, 18 Oct 2024 00:08:18 +0200 Subject: [PATCH 15/20] update docstring --- base/runtime_internals.jl | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index ac424df4be577..4471638f49ba1 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -966,6 +966,19 @@ typeintersect(@nospecialize(a), @nospecialize(b)) = (@_total_meta; ccall(:jl_typ morespecific(@nospecialize(a), @nospecialize(b)) = (@_total_meta; ccall(:jl_type_morespecific, Cint, (Any, Any), a::Type, b::Type) != 0) morespecific(a::Method, b::Method) = ccall(:jl_method_morespecific, Cint, (Any, Any), a, b) != 0 +""" + morespecific!(m::Method) + +Disallow adding methods more specific than `m`. +""" +function morespecific!(m::Method) + mt = get_methodtable(m) + # check that all Methods in this table are morespecific than m + # so we might avoid disabling a table that might get used for more than just subsets of m + all(m2 -> m === m2 || morespecific(m2, m), MethodList(mt)) || error("unsupported Method to disable") + setfield!(mt, nfields(mt), 0x1) +end + """ fieldoffset(type, i) @@ -1182,21 +1195,6 @@ function signature_type(@nospecialize(f), @nospecialize(argtypes)) return rewrap_unionall(Tuple{ft, u.parameters...}, argtypes) end -""" - freeze!(mt::Core.MethodTable) - -Disallow adding or modifying methods of `mt`. - -See also [`unfreeze!(mt)`](@ref unfreeze!). -""" -function morespecific!(m::Method) - mt = get_methodtable(m) - # check that all Methods in this table are morespecific than m - # so we might avoid disabling a table that might get used for more than just subsets of m - all(m2 -> m === m2 || morespecific(m2, m), MethodList(mt)) || error("unsupported Method to disable") - setfield!(mt, nfields(mt), 0x1) -end - function get_methodtable(m::Method) mt = ccall(:jl_method_get_table, Any, (Any,), m) if mt === nothing From ed9ec6edec5d3d2e70f19b3bab6a82b4022eb4d3 Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Fri, 18 Oct 2024 00:29:23 +0200 Subject: [PATCH 16/20] rm jl_method_table_set_frozen --- src/gf.c | 5 ----- src/jl_exported_funcs.inc | 1 - 2 files changed, 6 deletions(-) diff --git a/src/gf.c b/src/gf.c index cdebc53259430..abdda56730580 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2028,11 +2028,6 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho JL_UNLOCK(&world_counter_lock); } -JL_DLLEXPORT void jl_method_table_set_frozen(jl_methtable_t *mt, int val) -{ - mt->frozen = val; -} - static int jl_type_intersection2(jl_value_t *t1, jl_value_t *t2, jl_value_t **isect JL_REQUIRE_ROOTED_SLOT, jl_value_t **isect2 JL_REQUIRE_ROOTED_SLOT) { *isect2 = NULL; diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 5ae8b661deb69..f712f154ed896 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -305,7 +305,6 @@ XX(jl_method_instance_add_backedge) \ XX(jl_method_table_add_backedge) \ XX(jl_method_table_disable) \ - XX(jl_method_table_set_frozen) \ XX(jl_method_table_for) \ XX(jl_method_table_insert) \ XX(jl_methtable_lookup) \ From ebb41506c98ab4aae6635b38fa76839cdc6e8838 Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Fri, 18 Oct 2024 01:09:09 +0200 Subject: [PATCH 17/20] add another test --- test/reflection.jl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/reflection.jl b/test/reflection.jl index 911e6009e80b5..6855cf969b453 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -1301,7 +1301,12 @@ end # disallow adding new methods f_sealed(x::Int) = x+1 -Base.morespecific!(which(f_frozen, (Int,))) +f_sealed(x::Integer) = x+2 +@test_throws( + ErrorException("unsupported Method to disable"), + Base.morespecific!(which(f_sealed, (Int,))) +) +Base.morespecific!(which(f_sealed, (Integer,))) @test_throws( ErrorException("cannot add methods to or modify methods of a frozen function"), @eval f_sealed(x::Float64) = x+2 From b8f989c6e97e356a38b665b587e12ea1c422866c Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Fri, 18 Oct 2024 01:20:20 +0200 Subject: [PATCH 18/20] make jl_methtable_t->frozen an atomic field --- base/runtime_internals.jl | 2 +- src/datatype.c | 2 +- src/gf.c | 4 ++-- src/jltypes.c | 4 ++-- src/julia.h | 2 +- src/method.c | 2 +- src/staticdata.c | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 4471638f49ba1..0765960c97ab3 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -976,7 +976,7 @@ function morespecific!(m::Method) # check that all Methods in this table are morespecific than m # so we might avoid disabling a table that might get used for more than just subsets of m all(m2 -> m === m2 || morespecific(m2, m), MethodList(mt)) || error("unsupported Method to disable") - setfield!(mt, nfields(mt), 0x1) + setfield!(mt, :frozen, 0x1, :monotonic) end """ diff --git a/src/datatype.c b/src/datatype.c index c78b00fdd2245..44dedce8a3c02 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -54,7 +54,7 @@ JL_DLLEXPORT jl_methtable_t *jl_new_method_table(jl_sym_t *name, jl_module_t *mo mt->backedges = NULL; JL_MUTEX_INIT(&mt->writelock, "methodtable->writelock"); mt->offs = 0; - mt->frozen = 0; + jl_atomic_store_relaxed(&mt->frozen, 0); return mt; } diff --git a/src/gf.c b/src/gf.c index abdda56730580..fcf25fb40dd39 100644 --- a/src/gf.c +++ b/src/gf.c @@ -331,7 +331,7 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a (jl_value_t*)mi, 1, ~(size_t)0); jl_typemap_insert(&mt->cache, (jl_value_t*)mt, newentry, 0); - mt->frozen = 1; + jl_atomic_store_relaxed(&mt->frozen, 1); JL_GC_POP(); return dt; } @@ -772,7 +772,7 @@ static int reset_mt_caches(jl_methtable_t *mt, void *env) { // removes all method caches // this might not be entirely safe (GC or MT), thus we only do it very early in bootstrapping - if (!mt->frozen) { // make sure not to reset frozen functions + if (!jl_atomic_load_relaxed(&mt->frozen)) { // make sure not to reset frozen functions jl_atomic_store_release(&mt->leafcache, (jl_genericmemory_t*)jl_an_empty_memory_any); jl_atomic_store_release(&mt->cache, jl_nothing); } diff --git a/src/jltypes.c b/src/jltypes.c index 11f1d11a14edc..8b6e50dffdb8e 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3038,9 +3038,9 @@ void jl_init_types(void) JL_GC_DISABLED jl_methtable_type->name->names = jl_perm_symsvec(11, "name", "defs", "leafcache", "cache", "max_args", "module", "backedges", - "", "", "offs", ""); + "", "", "offs", "frozen"); const static uint32_t methtable_constfields[1] = { 0x00000020 }; // (1<<5); - const static uint32_t methtable_atomicfields[1] = { 0x0000001e }; // (1<<1)|(1<<2)|(1<<3)|(1<<4); + const static uint32_t methtable_atomicfields[1] = { 0x00000041e }; // (1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<10); jl_methtable_type->name->constfields = methtable_constfields; jl_methtable_type->name->atomicfields = methtable_atomicfields; jl_precompute_memoized_dt(jl_methtable_type, 1); diff --git a/src/julia.h b/src/julia.h index 7bb5f31eda708..837724cc5253d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -791,7 +791,7 @@ typedef struct _jl_methtable_t { jl_array_t *backedges; // (sig, caller::MethodInstance) pairs jl_mutex_t writelock; uint8_t offs; // 0, or 1 to skip splitting typemap on first (function) argument - uint8_t frozen; // whether this accepts adding new methods + _Atomic(uint8_t) frozen; // whether this accepts adding new methods } jl_methtable_t; typedef struct { diff --git a/src/method.c b/src/method.c index 10c7e3a2ddab9..0c3b14d064b17 100644 --- a/src/method.c +++ b/src/method.c @@ -1226,7 +1226,7 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, mt = jl_method_table_for(argtype); if ((jl_value_t*)mt == jl_nothing) jl_error("Method dispatch is unimplemented currently for this method signature"); - if (mt->frozen) + if (jl_atomic_load_acquire(&mt->frozen)) jl_error("cannot add methods to or modify methods of a frozen function"); assert(jl_is_linenode(functionloc)); diff --git a/src/staticdata.c b/src/staticdata.c index af4527cbc143f..a432dd0ad3cde 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -975,7 +975,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ } else if (jl_typetagis(v, jl_typename_type)) { jl_typename_t *tn = (jl_typename_t*)v; - if (tn->mt != NULL && !tn->mt->frozen) { + if (tn->mt != NULL && !jl_atomic_load_relaxed(&tn->mt->frozen)) { jl_methtable_t * new_methtable = (jl_methtable_t *)ptrhash_get(&new_methtables, tn->mt); if (new_methtable != HT_NOTFOUND) record_field_change((jl_value_t **)&tn->mt, (jl_value_t*)new_methtable); From 1de36808d673c74b7656ae5a73ef846fd55fa380 Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Wed, 23 Oct 2024 20:45:10 +0200 Subject: [PATCH 19/20] update test --- test/core.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core.jl b/test/core.jl index 1ff4afe99c687..8440f4f21902a 100644 --- a/test/core.jl +++ b/test/core.jl @@ -35,7 +35,7 @@ for (T, c) in ( (Core.CodeInstance, [:next, :min_world, :max_world, :inferred, :debuginfo, :ipo_purity_bits, :invoke, :specptr, :specsigflags, :precompile]), (Core.Method, [:primary_world, :deleted_world]), (Core.MethodInstance, [:cache, :flags]), - (Core.MethodTable, [:defs, :leafcache, :cache, :max_args]), + (Core.MethodTable, [:defs, :leafcache, :cache, :max_args, :frozen]), (Core.TypeMapEntry, [:next, :min_world, :max_world]), (Core.TypeMapLevel, [:arg1, :targ, :name1, :tname, :list, :any]), (Core.TypeName, [:cache, :linearcache]), From 6d99ec20b19739622224395c8b0f81b40aeb0cc5 Mon Sep 17 00:00:00 2001 From: Florian Atteneder Date: Wed, 23 Oct 2024 20:53:11 +0200 Subject: [PATCH 20/20] add assert(mt != NULL) to make gc-analyzer happy --- src/method.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/method.c b/src/method.c index 0c3b14d064b17..a0badc5b11e8d 100644 --- a/src/method.c +++ b/src/method.c @@ -1236,6 +1236,7 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, int32_t line = jl_linenode_line(functionloc); // TODO: derive our debug name from the syntax instead of the type + assert(mt != NULL); jl_methtable_t *kwmt = mt == jl_kwcall_mt ? jl_kwmethod_table_for(argtype) : mt; // if we have a kwcall, try to derive the name from the callee argument method table name = (kwmt ? kwmt : mt)->name;