Skip to content

Commit fcd09ec

Browse files
committed
Fix backward compat for CodeInfo serialization
Here we introduce an explicit codeinfo_ver version number to track the CodeInfo struct. Core.CodeInfo has seen multiple changes between Julia versions and the addition of the hide_in_stacktrace flag is not possible to detect via the types which exist in the serialized stream. To solve this problem and to avoid backward compatibility code becoming more convoluted and hard to maintain in the future, it seems simplest to introduce an Int32 version flag which should be a single byte of overhead. An alternative which was considered was to rely on the value of the header ser_version. However, this would need to be somehow stored with any user-defined subtype of AbstractSerializer which would be a API breaking change.
1 parent f38fda7 commit fcd09ec

File tree

2 files changed

+116
-11
lines changed

2 files changed

+116
-11
lines changed

stdlib/Serialization/src/Serialization.jl

+68-3
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ const TAGS = Any[
7777

7878
@assert length(TAGS) == 255
7979

80-
const ser_version = 10 # do not make changes without bumping the version #!
80+
const ser_version = 11 # do not make changes without bumping the version #!
8181

8282
const NTAGS = length(TAGS)
8383

@@ -109,6 +109,7 @@ const ARRAY_TAG = findfirst(==(Array), TAGS)%Int32
109109
const EXPR_TAG = sertag(Expr)
110110
const MODULE_TAG = sertag(Module)
111111
const METHODINSTANCE_TAG = sertag(Core.MethodInstance)
112+
const CODEINFO_TAG = sertag(Core.CodeInfo)
112113
const METHOD_TAG = sertag(Method)
113114
const TASK_TAG = sertag(Task)
114115
const DATATYPE_TAG = sertag(DataType)
@@ -438,6 +439,41 @@ function serialize(s::AbstractSerializer, linfo::Core.MethodInstance)
438439
nothing
439440
end
440441

442+
# For future compatibility detection, add a version specifically for CodeInfo.
443+
# Bump this each time CodeInfo changes, and add a deserializer for the old
444+
# version.
445+
# 1 - julia 1.0/1.1 - not stored; must be inferred
446+
# 2 - julia 1.2 - not stored; must be inferred
447+
# 3 - julia 1.3/1.4 - not stored; must be inferred
448+
# 4 - julia 1.5
449+
const _current_codeinfo_ver = Int32(4)
450+
451+
function serialize(s::AbstractSerializer, ci::Core.CodeInfo)
452+
serialize_cycle(s, ci) && return
453+
writetag(s.io, CODEINFO_TAG)
454+
serialize(s, _current_codeinfo_ver) # for backwards compat
455+
serialize(s, ci.code)
456+
serialize(s, ci.codelocs)
457+
serialize(s, ci.ssavaluetypes)
458+
serialize(s, ci.ssaflags)
459+
serialize(s, ci.method_for_inference_limit_heuristics)
460+
serialize(s, ci.linetable)
461+
serialize(s, ci.slotnames)
462+
serialize(s, ci.slotflags)
463+
serialize(s, ci.slottypes)
464+
serialize(s, ci.rettype)
465+
serialize(s, ci.parent)
466+
serialize(s, ci.edges)
467+
serialize(s, ci.min_world)
468+
serialize(s, ci.max_world)
469+
serialize(s, ci.hide_in_stacktrace)
470+
serialize(s, ci.inferred)
471+
serialize(s, ci.inlineable)
472+
serialize(s, ci.propagate_inbounds)
473+
serialize(s, ci.pure)
474+
nothing
475+
end
476+
441477
function serialize(s::AbstractSerializer, t::Task)
442478
serialize_cycle(s, t) && return
443479
if istaskstarted(t) && !istaskdone(t)
@@ -1018,7 +1054,37 @@ end
10181054
function deserialize(s::AbstractSerializer, ::Type{CodeInfo})
10191055
ci = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ())
10201056
deserialize_cycle(s, ci)
1021-
ci.code = deserialize(s)::Vector{Any}
1057+
code_or_codeinfo_ver = deserialize(s)
1058+
if code_or_codeinfo_ver isa Vector{Any}
1059+
ci.code = code_or_codeinfo_ver
1060+
_deserialize_old_codeinfo!(s, ci)
1061+
return ci
1062+
end
1063+
codeinfo_ver = code_or_codeinfo_ver::Int32
1064+
@assert codeinfo_ver == _current_codeinfo_ver
1065+
ci.code = deserialize(s)
1066+
ci.codelocs = deserialize(s)
1067+
ci.ssavaluetypes = deserialize(s)
1068+
ci.ssaflags = deserialize(s)
1069+
ci.method_for_inference_limit_heuristics = deserialize(s)
1070+
ci.linetable = deserialize(s)
1071+
ci.slotnames = deserialize(s)
1072+
ci.slotflags = deserialize(s)
1073+
ci.slottypes = deserialize(s)
1074+
ci.rettype = deserialize(s)
1075+
ci.parent = deserialize(s)
1076+
ci.edges = deserialize(s)
1077+
ci.min_world = deserialize(s)
1078+
ci.max_world = deserialize(s)
1079+
ci.hide_in_stacktrace = deserialize(s)
1080+
ci.inferred = deserialize(s)
1081+
ci.inlineable = deserialize(s)
1082+
ci.propagate_inbounds = deserialize(s)
1083+
ci.pure = deserialize(s)
1084+
return ci
1085+
end
1086+
1087+
function _deserialize_old_codeinfo!(s, ci)
10221088
ci.codelocs = deserialize(s)::Vector{Int32}
10231089
_x = deserialize(s)
10241090
if _x isa Array || _x isa Int
@@ -1054,7 +1120,6 @@ function deserialize(s::AbstractSerializer, ::Type{CodeInfo})
10541120
ci.max_world = reinterpret(UInt, deserialize(s))
10551121
end
10561122
end
1057-
ci.hide_in_stacktrace = deserialize(s)
10581123
ci.inferred = deserialize(s)
10591124
ci.inlineable = deserialize(s)
10601125
ci.propagate_inbounds = deserialize(s)

stdlib/Serialization/test/runtests.jl

+48-8
Original file line numberDiff line numberDiff line change
@@ -563,15 +563,55 @@ let f = tempname(), x = [rand(2,2), :x, "hello"]
563563
rm(f)
564564
end
565565

566-
let f_data
567-
# a serialized function from v1.0/v1.1
568-
if Int === Int64
569-
f_data = "N0pMBwQAAAA0MxMAAAAAAAAAAAEFIyM1IzYiAAAAABBYH04BBE1haW6bRCIAAAAAIgAAAABNTEy+AQIjNRUAI+AjAQAAAAAAAAAfTgEETWFpbkQBAiM1AQdSRVBMWzNdvxBTH04BBE1haW6bRAMAAAAzLAAARkYiAAAAAE7BTBsVRuIWA1YkH04BBE1haW5EAQEq4SXhFgNWJB9OAQRNYWluRJ0o4CXiFgFVKOEVAAbiAQAAAAEAAAABAAAATuIVRuA0EAEMTGluZUluZm9Ob2RlH04BBE1haW6bRB9OAQRNYWluRAECIzUBB1JFUExbM13g3xXfFeIAAAAVRuKifX5MTExMTuIp"
570-
else
571-
f_data = "N0pMBwAAAAA0MxMAAAAAAAAAAAEFIyM1IzYiAAAAABBYH04BBE1haW6bRCIAAAAAIgAAAABNTEy+AQIjNRUAI78jAQAAAAAAAAAfTgEETWFpbkQBAiM1AQdSRVBMWzJdvxBTH04BBE1haW6bRAMAAAAzLAAARkYiAAAAAE7BTBsVRsEWA1YkH04BBE1haW5EAQEqwCXAFgNWJB9OAQRNYWluRJ0ovyXBFgFVKMAVAAbBAQAAAAEAAAABAAAATsEVRr80EAEMTGluZUluZm9Ob2RlH04BBE1haW6bRB9OAQRNYWluRAECIzUBB1JFUExbMl2/vhW+FcEAAAAVRsGifX5MTExMTsEp"
566+
@testset "Closure serialization" begin
567+
# Serialized versions of the closure
568+
# (x,y)-> 2x + y
569+
# from various versions of juila
570+
let f_data
571+
# From v1.0/v1.1
572+
if Int === Int64
573+
f_data = "N0pMBwQAAAA0MxMAAAAAAAAAAAEFIyM1IzYiAAAAABBYH04BBE1haW6bRCIAAAAAIgAAAABNTEy+AQIjNRUAI+AjAQAAAAAAAAAfTgEETWFpbkQBAiM1AQdSRVBMWzNdvxBTH04BBE1haW6bRAMAAAAzLAAARkYiAAAAAE7BTBsVRuIWA1YkH04BBE1haW5EAQEq4SXhFgNWJB9OAQRNYWluRJ0o4CXiFgFVKOEVAAbiAQAAAAEAAAABAAAATuIVRuA0EAEMTGluZUluZm9Ob2RlH04BBE1haW6bRB9OAQRNYWluRAECIzUBB1JFUExbM13g3xXfFeIAAAAVRuKifX5MTExMTuIp"
574+
else
575+
f_data = "N0pMBwAAAAA0MxMAAAAAAAAAAAEFIyM1IzYiAAAAABBYH04BBE1haW6bRCIAAAAAIgAAAABNTEy+AQIjNRUAI78jAQAAAAAAAAAfTgEETWFpbkQBAiM1AQdSRVBMWzJdvxBTH04BBE1haW6bRAMAAAAzLAAARkYiAAAAAE7BTBsVRsEWA1YkH04BBE1haW5EAQEqwCXAFgNWJB9OAQRNYWluRJ0ovyXBFgFVKMAVAAbBAQAAAAEAAAABAAAATsEVRr80EAEMTGluZUluZm9Ob2RlH04BBE1haW6bRB9OAQRNYWluRAECIzUBB1JFUExbMl2/vhW+FcEAAAAVRsGifX5MTExMTsEp"
576+
end
577+
f = deserialize(IOBuffer(base64decode(f_data)))
578+
@test f(10,3) == 23
579+
end
580+
581+
let f_data
582+
# From v1.2.0
583+
if Int === Int64
584+
f_data = "N0pMCAQAAAA0MxMCAAAAAAAAAAEHIyMxMSMxMiIAAAAAEFgfTgEETWFpbptEIgAAAAAiAAAAAE1MTL4BAyMxMRUAI+AjAwAAAAAAAAAfTgEETWFpbkQBAyMxMQEIUkVQTFsxM12/EFMfTgEETWFpbptEAwAAADMsAABGRiELI3NlbGYjAHgAeQDBTBsVRuIWA1YkH04BBE1haW5EAQEq4SXhFgNWJB9OAQRNYWluRJ0o4CXiFgFVKOEVAAbiAQAAAAEAAAABAAAA4hXfThVG4DQQAQxMaW5lSW5mb05vZGUfTgEETWFpbptEAQMjMTEsBADg3xUAAeKifX4V4gAICE5GTuAx/////0xMTExO4ik="
585+
# Found to be broken on 2020-05-06
586+
@test_broken begin
587+
f = deserialize(IOBuffer(base64decode(f_data)))
588+
f(10,3)
589+
end == 23
590+
end
591+
end
592+
593+
let f_data
594+
# From v1.3 (v1.4 compatible)
595+
# (x,y)-> 2x + y
596+
if Int === Int64
597+
f_data = "N0pMCAQAAAA0MxMAAAAAAAAAAAEFIzkjMTAiAAAAABBYH04BBE1haW6bRCIAAAAAIgAAAABNTEy+AQIjORUAI+AjAQAAAAAAAAAfTgEETWFpbkQBAiM5AQdSRVBMWzNdvxBTH04BBE1haW6bRAMAAAAzLAAARkYhCyNzZWxmIwB4AHkAwUwbFUbiFgNWJB9OAQRNYWluRAEBKuEl4RYDViQfTgEETWFpbkSdKOAl4hYBVSjhFQAG4gEAAAABAAAAAQAAAOIV304VRuA0EAEMTGluZUluZm9Ob2RlH04BBE1haW6bRAECIzkBB1JFUExbM13g3xUAAeKifX4V4gAICE5GTk4JAQAAAAAAAAAJ//////////9MTExMTuIp"
598+
else
599+
f_data = "N0pMCAAAAAA0MxMAAAAAAAAAAAEFIzkjMTAiAAAAABBYH04BBE1haW6bRCIAAAAAIgAAAABNTEy+AQIjORUAI78jAQAAAAAAAAAfTgEETWFpbkQBAiM5AQdSRVBMWzJdvxBTH04BBE1haW6bRAMAAAAzLAAARkYhCyNzZWxmIwB4AHkAwUwbFUbBFgNWJB9OAQRNYWluRAEBKsAlwBYDViQfTgEETWFpbkSdKL8lwRYBVSjAFQAGwQEAAAABAAAAAQAAAMEVvk4VRr80EAEMTGluZUluZm9Ob2RlH04BBE1haW6bRAECIzkBB1JFUExbMl2/vhUAAcGifX4VwQAICE5GTk4HAQAAAAf/////TExMTE7BKQ=="
600+
end
601+
@test begin
602+
f = deserialize(IOBuffer(base64decode(f_data)))
603+
f(10,3)
604+
end == 23
605+
end
606+
607+
let f_data
608+
# From v1.5-DEV (codeinfo_ver = 4)
609+
if Int === Int64
610+
f_data = "N0pMCwQAAAA0MxMAAAAAAAAAAAEEIzcjOCIAAAAAEFgfTptEIgAAAAAiAAAAAE1MTL4BAiM3FQAj4CMBAAAAAAAAAB9OAQRNYWluRAECIzcBB1JFUExbMl2/EFMfTptEAwAAADMsAABGRjkhCyNzZWxmIwB4AHkAwUwbwhVG4hYDViQfTgEETWFpbkQBASrhJeEWA1YkH04BBE1haW5EnSjgJeIWAVUo4RUABuIBAAAAAQAAAAEAAADiFd9OFUbgNBABDExpbmVJbmZvTm9kZR9Om0QBAiM3AQdSRVBMWzJd4N8VAAHion1+FeIACAhORk5OCQEAAAAAAAAACf//////////TExMTExO4ik="
611+
f = deserialize(IOBuffer(base64decode(f_data)))
612+
@test f(10,3) == 23
613+
end
572614
end
573-
f = deserialize(IOBuffer(base64decode(f_data)))
574-
@test f(10,3) == 23
575615
end
576616

577617
# issue #33466, IdDict

0 commit comments

Comments
 (0)