Skip to content

Commit 3f25fc1

Browse files
committed
Re-factor: Rename parselog to import_abi_info
Also add a struct definition to be able to conveniently document the various bits of output data.
1 parent 9b6ffe9 commit 3f25fc1

File tree

4 files changed

+84
-55
lines changed

4 files changed

+84
-55
lines changed

src/JuliaLibWrapping.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ module JuliaLibWrapping
33
using OrderedCollections
44
using Graphs
55

6-
export parselog, wrapper
7-
export CProject
6+
export import_abi_info, wrapper
7+
export CProject, ABIInfo
88

9-
include("parselog.jl")
9+
include("abi_import.jl")
1010
include("c.jl")
1111

1212
end

src/parselog.jl renamed to src/abi_import.jl

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -113,26 +113,16 @@ function build_type_graph(typedescs::OrderedDict{Int, TypeDesc};
113113
end
114114

115115
"""
116-
entrypoints, typedescs = parselog(filename::String)
116+
sort_declarations!(typedescs) -> forward_declarations
117117
118-
Extract the signatures of the entrypoints and nonstandard types from a log file.
119-
"""
120-
function parselog(filename::String)
121-
abi_info = JSON.Parser.parsefile(filename)
122-
123-
# Extract all the type descriptors
124-
typedescs = OrderedDict{Int, TypeDesc}()
125-
for type in abi_info["types"]
126-
id = type["id"]::Int
127-
typedescs[id] = from_json(TypeDesc, type)
128-
end
129-
130-
# Then collect the methods
131-
entrypoints = MethodDesc[]
132-
for method in abi_info["functions"]
133-
push!(entrypoints, from_json(MethodDesc, method))
134-
end
118+
Sort `typedescs` w.r.t. type-dependencies (e.g. Type A using Type B in a field),
119+
so that a type-descriptor always appears after any dependencies. Sort is performed
120+
in-place.
135121
122+
Returns indices of all types that could not be sorted (due to recursive types, e.g.
123+
a linked list in C). In C, these are the definitions that must be forward-declared.
124+
"""
125+
function sort_declarations!(typedescs::OrderedDict{Int, TypeDesc})
136126
# First we have to identify the parts of the graph where we have type-recursion and
137127
# therefore have to use forward-declarations instead of just sorting declarations.
138128
recursive_types = BitSet()
@@ -178,6 +168,42 @@ function parselog(filename::String)
178168
end
179169
end
180170

181-
return entrypoints, typedescs, forwarddecls
171+
return forwarddecls
172+
end
173+
174+
struct ABIInfo
175+
# A map from `type_id` to `type_descriptor`, sorted by declaration order.
176+
typeinfo::OrderedDict{Int, TypeDesc}
177+
# Indexes into `types`, indicates which types must be forward-declared for C.
178+
forward_declared::BitSet
179+
# A vector of exposed functions from the imported ABI
180+
entrypoints::Vector{MethodDesc}
181+
end
182+
183+
"""
184+
entrypoints, typedescs, forward_declarations = parselog(filename::String)
185+
186+
Extract the signatures of the entrypoints and types from an exported ABI info (JSON) file.
187+
"""
188+
function import_abi_info(filename::String)
189+
abi_info = JSON.Parser.parsefile(filename)
190+
191+
# Extract all the type descriptors
192+
typedescs = OrderedDict{Int, TypeDesc}()
193+
for type in abi_info["types"]
194+
id = type["id"]::Int
195+
typedescs[id] = from_json(TypeDesc, type)
196+
end
197+
198+
# Then collect the methods
199+
entrypoints = MethodDesc[]
200+
for method in abi_info["functions"]
201+
push!(entrypoints, from_json(MethodDesc, method))
202+
end
203+
204+
forward_declared = sort_declarations!(typedescs)
205+
206+
return ABIInfo(typedescs, forward_declared, entrypoints)
182207
end
183-
parselog(filename::AbstractString) = parselog(String(filename)::String)
208+
209+
import_abi_info(filename::AbstractString) = import_abi_info(String(filename)::String)

src/c.jl

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ struct CProject
33
headerbase::String
44
end
55

6-
function wrapper(dest::CProject, entrypoints, typedescs, forwarddecls)
6+
function wrapper(dest::CProject, abi_info::ABIInfo)
7+
(; entrypoints, typeinfo, forward_declared) = abi_info
8+
79
# Write the header file for C
810
headerfile = joinpath(dest.dir, dest.headerbase * ".h")
911
libvar = "JULIALIB_" * uppercase(dest.headerbase) * "_H"
@@ -18,20 +20,20 @@ function wrapper(dest::CProject, entrypoints, typedescs, forwarddecls)
1820
typedict = Dict{Int, String}()
1921

2022
# Print forward-declarations (if any, for recursive types)
21-
for id in forwarddecls
22-
type = typedescs[id]
23+
for id in forward_declared
24+
type = typeinfo[id]
2325
@assert type isa StructDesc "un-expected forward declaration type"
24-
mangled_name = mangle_c!(typedict, id, typedescs)
26+
mangled_name = mangle_c!(typedict, id, typeinfo)
2527
println(f, "struct ", mangled_name, ";")
2628
end
2729

2830
# Print the struct definitions
29-
for (id, type) in pairs(typedescs)
31+
for (id, type) in pairs(typeinfo)
3032
if type isa StructDesc
31-
mangled_name = mangle_c!(typedict, id, typedescs)
33+
mangled_name = mangle_c!(typedict, id, typeinfo)
3234
println(f, "typedef struct ", mangled_name, " {")
3335
for field in type.fields
34-
ft = mangle_c!(typedict, field.type, typedescs)
36+
ft = mangle_c!(typedict, field.type, typeinfo)
3537
println(f, " ", ft, " ", sanitize_for_c(field.name), ";")
3638
end
3739
println(f, "} ", mangled_name, ";")
@@ -48,7 +50,7 @@ function wrapper(dest::CProject, entrypoints, typedescs, forwarddecls)
4850
if !isempty(args)
4951
args = ", " * args
5052
end
51-
mangled_rt = mangle_c!(typedict, method.return_type, typedescs)
53+
mangled_rt = mangle_c!(typedict, method.return_type, typeinfo)
5254
print(f, mangled_rt, " ", method.symbol, "(")
5355
isfirst = true
5456
for arg in method.args
@@ -57,7 +59,7 @@ function wrapper(dest::CProject, entrypoints, typedescs, forwarddecls)
5759
else
5860
print(f, ", ")
5961
end
60-
ft = mangle_c!(typedict, arg.type, typedescs)
62+
ft = mangle_c!(typedict, arg.type, typeinfo)
6163
print(f, ft, " ", sanitize_for_c(arg.name))
6264
if arg.isva
6365
print(f, "...")
@@ -107,19 +109,19 @@ function sanitize_for_c(str::AbstractString)
107109
return replace(str, r"[^a-zA-Z0-9_]" => "_")
108110
end
109111

110-
function mangle_c!(typedict::Dict{Int, String}, type_id::Int, typedescs::OrderedDict{Int,TypeDesc})
112+
function mangle_c!(typedict::Dict{Int, String}, type_id::Int, typeinfo::OrderedDict{Int,TypeDesc})
111113
if type_id in keys(typedict)
112114
return typedict[type_id]
113115
end
114116

115-
type = typedescs[type_id]
117+
type = typeinfo[type_id]
116118
if type isa PrimitiveTypeDesc
117119
if !in(type.name, keys(ctypes))
118120
error("unsupported primitive type: '$(type.name)'")
119121
end
120122
return ctypes[type.name]
121123
elseif type isa PointerDesc
122-
mangled = mangle_c!(typedict, type.pointee_type, typedescs) * "*"
124+
mangled = mangle_c!(typedict, type.pointee_type, typeinfo) * "*"
123125
elseif type isa StructDesc
124126
mangled = sanitize_for_c(type.name)
125127
end

test/runtests.jl

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,71 +10,72 @@ function onlymatch(f, collection)
1010
end
1111

1212
@testset "JuliaLibWrapping.jl" begin
13-
@testset "parselog" begin
14-
entrypoints, typedescs, forwarddecls = parselog("bindinginfo_libsimple.json")
13+
@testset "import_abi_info" begin
14+
abi_info = import_abi_info("bindinginfo_libsimple.json")
15+
(; entrypoints, typeinfo) = abi_info
1516

1617
methdesc = onlymatch(md -> md.symbol == "copyto_and_sum", entrypoints)
17-
@test typedescs[methdesc.return_type].name == "Float32"
18+
@test typeinfo[methdesc.return_type].name == "Float32"
1819
@test length(methdesc.args) == 1
1920
argdesc = only(methdesc.args)
2021
@test argdesc.name == "fromto"
21-
@test typedescs[argdesc.type].name == "CVectorPair{Float32}"
22+
@test typeinfo[argdesc.type].name == "CVectorPair{Float32}"
2223
@test argdesc.isva == false
2324

2425
methdesc = onlymatch(md -> md.symbol == "countsame", entrypoints)
25-
@test typedescs[methdesc.return_type].name == "Int32"
26+
@test typeinfo[methdesc.return_type].name == "Int32"
2627
@test length(methdesc.args) == 2
2728
argdesc1, argdesc2 = methdesc.args
2829
@test argdesc1.name == "list"
29-
@test typedescs[argdesc1.type].name == "Ptr{MyTwoVec}"
30+
@test typeinfo[argdesc1.type].name == "Ptr{MyTwoVec}"
3031
@test argdesc1.isva == false
3132
@test argdesc2.name == "n"
32-
@test typedescs[argdesc2.type].name == "Int32"
33+
@test typeinfo[argdesc2.type].name == "Int32"
3334
@test argdesc2.isva == false
3435

35-
@test length(typedescs) >= 3
36+
@test length(typeinfo) >= 3
3637
findtype(descs, name) = (k = collect(keys(descs)); k[findfirst((id)->descs[id].name === name, k)])
3738

38-
tdesc = typedescs[findtype(typedescs, "CVectorPair{Float32}")]
39+
tdesc = typeinfo[findtype(typeinfo, "CVectorPair{Float32}")]
3940
@test tdesc.name == "CVectorPair{Float32}"
4041
@test length(tdesc.fields) == 2
4142
@test tdesc.fields[1].name == "from"
42-
@test typedescs[tdesc.fields[1].type].name == "CVector{Float32}"
43+
@test typeinfo[tdesc.fields[1].type].name == "CVector{Float32}"
4344
@test tdesc.fields[1].offset == 0
4445
@test tdesc.fields[2].name == "to"
45-
@test typedescs[tdesc.fields[2].type].name == "CVector{Float32}"
46+
@test typeinfo[tdesc.fields[2].type].name == "CVector{Float32}"
4647
@test tdesc.fields[2].offset == 16
4748
@test tdesc.size == 32
48-
tdesc = typedescs[findtype(typedescs, "CVector{Float32}")]
49+
tdesc = typeinfo[findtype(typeinfo, "CVector{Float32}")]
4950
@test tdesc.name == "CVector{Float32}"
5051
@test length(tdesc.fields) == 2
5152
@test tdesc.fields[1].name == "length"
52-
@test typedescs[tdesc.fields[1].type].name == "Int32"
53+
@test typeinfo[tdesc.fields[1].type].name == "Int32"
5354
@test tdesc.fields[1].offset == 0
5455
@test tdesc.fields[2].name == "data"
55-
@test typedescs[tdesc.fields[2].type].name == "Ptr{Float32}"
56+
@test typeinfo[tdesc.fields[2].type].name == "Ptr{Float32}"
5657
@test tdesc.fields[2].offset == 8
5758
@test tdesc.size == 16
58-
tdesc = typedescs[findtype(typedescs, "MyTwoVec")]
59+
tdesc = typeinfo[findtype(typeinfo, "MyTwoVec")]
5960
@test tdesc.name == "MyTwoVec"
6061
@test length(tdesc.fields) == 2
6162
@test tdesc.fields[1].name == "x"
62-
@test typedescs[tdesc.fields[1].type].name == "Int32"
63+
@test typeinfo[tdesc.fields[1].type].name == "Int32"
6364
@test tdesc.fields[1].offset == 0
6465
@test tdesc.fields[2].name == "y"
65-
@test typedescs[tdesc.fields[2].type].name == "Int32"
66+
@test typeinfo[tdesc.fields[2].type].name == "Int32"
6667
@test tdesc.fields[2].offset == 4
6768
@test tdesc.size == 8
68-
name2idx = Dict(desc.name => i for (i, desc) in enumerate(values(typedescs)))
69+
name2idx = Dict(desc.name => i for (i, desc) in enumerate(values(typeinfo)))
6970
@test name2idx["CVectorPair{Float32}"] > name2idx["CVector{Float32}"]
7071
end
7172

7273
@testset "C wrapper" begin
7374
mktempdir() do path
7475
mkpath(path)
7576
dest = CProject(path, "libsimple")
76-
entrypoints, typedescs, forwarddecls = parselog("bindinginfo_libsimple.json")
77-
wrapper(dest, entrypoints, typedescs, forwarddecls)
77+
abi_info = import_abi_info("bindinginfo_libsimple.json")
78+
wrapper(dest, abi_info)
7879

7980
headerfile = joinpath(dest.dir, dest.headerbase * ".h")
8081
@test isfile(headerfile)

0 commit comments

Comments
 (0)