Make a secret or black box function?

Note that with the IRTools or the JLD2 solutions (or really anything that gives you a pure Julia function), they’re a @code_warntype away from seeing (a partially obfuscated version of) the source of the top-level function call:

# with IRTools solution from above
julia> @code_warntype g(nothing,2,1)

Body::Float64
1 ─ %1 = Core.apply_type(Base.Val, 2)::Core.Const(Val{2})
│   %2 = (%1)()::Core.Const(Val{2}())
│   %3 = Base.literal_pow(Main.:^, @_3, %2)::Int64
│   %4 = Main.sin(@_4)::Float64
│   %5 = (%3 + %4)::Float64
└──      return %5

You can fix that by just wrapping the function in a closure, e.g.

julia> f = (x,y) -> (() -> x^2 + sin(y))();

At that point, you could just as well use the built-in Serialization to save the function:

julia> serialize("secret_function.jls", f);

julia> g = deserialize("secret_function.jls");

julia> @code_warntype g(1,2)

Body::Float64
1 ─ %1 = Serialization.__deserialized_types__.:(var"#14#16")::Core.Const(Serialization.__deserialized_types__.var"#14#16")
│   %2 = Core.typeof(x)::Core.Const(Int64)
│   %3 = Core.typeof(y)::Core.Const(Int64)
│   %4 = Core.apply_type(%1, %2, %3)::Core.Const(Serialization.__deserialized_types__.var"#14#16"{Int64, Int64})
│        (#14 = %new(%4, x, y))
│   %6 = #14::Serialization.__deserialized_types__.var"#14#16"{Int64, Int64}
│   %7 = (%6)()::Float64
└──      return %7

julia> g(1,2)
1.9092974268256817

The contents of the file is pretty unreadable (though they may see that e.g. sin is used in someway, but doubt anyone can easily tell how):

7JL
43#13#15"XN�D""MLL�#13#�#NMainD#13aREPL[4]�SN�D3,FF9!#self#xy#14�LeF�63#14#16"}~,"}GF~GF",	,
LLL�#14#�#aNMainD#14aREPL[4]�SN�D3,!a#self#�LeF�V$N�D�%�}V$N�D�$N�DVal�V(�V$N�Dliteral_pow$NMainD^(�(�V$N�D�%�~V$NMainDsin(�V$NMainD�(�(�:(����NF�4LineInfoNodeN�DNMainD#14aREPL[4]����NFNN		��������LLLLN�)V$N�Dtypeof%�V$N�Dtypeof%�V$N�D�(�(�(�Y%��(�%�%�%�V(�:(����NF�4,eNMainD#13aREPL[4]���}~#14�NFNN		��������LLLLN�)

Although even still, the function is technically reconstructable in Julia if they @code_warntype into the deserialized closure, etc… Only way to make this Julia-proof is probably provide a dynamic library for a C compiled function.

8 Likes