# This file is a part of Julia. License is MIT: https://julialang.org/license # timing # time() in libc.jl # high-resolution relative time, in nanoseconds """ time_ns() Get the time in nanoseconds. The time corresponding to 0 is undefined, and wraps every 5.8 years. """ time_ns() = ccall(:jl_hrtime, UInt64, ()) # This type must be kept in sync with the C struct in src/gc.h struct GC_Num allocd ::Int64 # GC internal deferred_alloc::Int64 # GC internal freed ::Int64 # GC internal malloc ::UInt64 realloc ::UInt64 poolalloc ::UInt64 bigalloc ::UInt64 freecall ::UInt64 total_time ::UInt64 total_allocd::UInt64 # GC internal since_sweep ::UInt64 # GC internal collect ::Csize_t # GC internal pause ::Cint full_sweep ::Cint end gc_num() = ccall(:jl_gc_num, GC_Num, ()) # This type is to represent differences in the counters, so fields may be negative struct GC_Diff allocd ::Int64 # Bytes allocated malloc ::Int64 # Number of GC aware malloc() realloc ::Int64 # Number of GC aware realloc() poolalloc ::Int64 # Number of pool allocation bigalloc ::Int64 # Number of big (non-pool) allocation freecall ::Int64 # Number of GC aware free() total_time ::Int64 # Time spent in garbage collection pause ::Int64 # Number of GC pauses full_sweep ::Int64 # Number of GC full collection end gc_total_bytes(gc_num::GC_Num) = (gc_num.allocd + gc_num.deferred_alloc + Int64(gc_num.collect) + Int64(gc_num.total_allocd)) function GC_Diff(new::GC_Num, old::GC_Num) # logic from `src/gc.c:jl_gc_total_bytes` old_allocd = gc_total_bytes(old) new_allocd = gc_total_bytes(new) return GC_Diff(new_allocd - old_allocd, Int64(new.malloc - old.malloc), Int64(new.realloc - old.realloc), Int64(new.poolalloc - old.poolalloc), Int64(new.bigalloc - old.bigalloc), Int64(new.freecall - old.freecall), Int64(new.total_time - old.total_time), new.pause - old.pause, new.full_sweep - old.full_sweep) end function gc_alloc_count(diff::GC_Diff) diff.malloc + diff.realloc + diff.poolalloc + diff.bigalloc end # total time spend in garbage collection, in nanoseconds gc_time_ns() = ccall(:jl_gc_total_hrtime, UInt64, ()) # total number of bytes allocated so far gc_bytes() = ccall(:jl_gc_total_bytes, Int64, ()) """ tic() Set a timer to be read by the next call to [`toc`](@ref) or [`toq`](@ref). The macro call `@time expr` can also be used to time evaluation. ```julia-repl julia> tic() 0x0000c45bc7abac95 julia> sleep(0.3) julia> toc() elapsed time: 0.302745944 seconds 0.302745944 ``` """ function tic() t0 = time_ns() task_local_storage(:TIMERS, (t0, get(task_local_storage(), :TIMERS, ()))) return t0 end """ toq() Return, but do not print, the time elapsed since the last [`tic`](@ref). The macro calls `@timed expr` and `@elapsed expr` also return evaluation time. ```julia-repl julia> tic() 0x0000c46477a9675d julia> sleep(0.3) julia> toq() 0.302251004 ``` """ function toq() t1 = time_ns() timers = get(task_local_storage(), :TIMERS, ()) if timers === () error("toc() without tic()") end t0 = timers[1]::UInt64 task_local_storage(:TIMERS, timers[2]) (t1-t0)/1e9 end """ toc() Print and return the time elapsed since the last [`tic`](@ref). The macro call `@time expr` can also be used to time evaluation. ```julia-repl julia> tic() 0x0000c45bc7abac95 julia> sleep(0.3) julia> toc() elapsed time: 0.302745944 seconds 0.302745944 ``` """ function toc() t = toq() println("elapsed time: ", t, " seconds") return t end # print elapsed time, return expression value const _mem_units = ["byte", "KiB", "MiB", "GiB", "TiB", "PiB"] const _cnt_units = ["", " k", " M", " G", " T", " P"] function prettyprint_getunits(value, numunits, factor) if value == 0 || value == 1 return (value, 1) end unit = ceil(Int, log(value) / log(factor)) unit = min(numunits, unit) number = value/factor^(unit-1) return number, unit end function padded_nonzero_print(value,str) if value != 0 blanks = " "[1:18-length(str)] println("$str:$blanks$value") end end function time_print(elapsedtime, bytes, gctime, allocs) @printf("%10.6f seconds", elapsedtime/1e9) if bytes != 0 || allocs != 0 bytes, mb = prettyprint_getunits(bytes, length(_mem_units), Int64(1024)) allocs, ma = prettyprint_getunits(allocs, length(_cnt_units), Int64(1000)) if ma == 1 @printf(" (%d%s allocation%s: ", allocs, _cnt_units[ma], allocs==1 ? "" : "s") else @printf(" (%.2f%s allocations: ", allocs, _cnt_units[ma]) end if mb == 1 @printf("%d %s%s", bytes, _mem_units[mb], bytes==1 ? "" : "s") else @printf("%.3f %s", bytes, _mem_units[mb]) end if gctime > 0 @printf(", %.2f%% gc time", 100*gctime/elapsedtime) end print(")") elseif gctime > 0 @printf(", %.2f%% gc time", 100*gctime/elapsedtime) end println() end function timev_print(elapsedtime, diff::GC_Diff) allocs = gc_alloc_count(diff) time_print(elapsedtime, diff.allocd, diff.total_time, allocs) print("elapsed time (ns): $elapsedtime\n") padded_nonzero_print(diff.total_time, "gc time (ns)") padded_nonzero_print(diff.allocd, "bytes allocated") padded_nonzero_print(diff.poolalloc, "pool allocs") padded_nonzero_print(diff.bigalloc, "non-pool GC allocs") padded_nonzero_print(diff.malloc, "malloc() calls") padded_nonzero_print(diff.realloc, "realloc() calls") padded_nonzero_print(diff.freecall, "free() calls") padded_nonzero_print(diff.pause, "GC pauses") padded_nonzero_print(diff.full_sweep, "full collections") end """ @time A macro to execute an expression, printing the time it took to execute, the number of allocations, and the total number of bytes its execution caused to be allocated, before returning the value of the expression. See also [`@timev`](@ref), [`@timed`](@ref), [`@elapsed`](@ref), and [`@allocated`](@ref). ```julia-repl julia> @time rand(10^6); 0.001525 seconds (7 allocations: 7.630 MiB) julia> @time begin sleep(0.3) 1+1 end 0.301395 seconds (8 allocations: 336 bytes) ``` """ macro time(ex) quote local stats = gc_num() local elapsedtime = time_ns() local val = $(esc(ex)) elapsedtime = time_ns() - elapsedtime local diff = GC_Diff(gc_num(), stats) time_print(elapsedtime, diff.allocd, diff.total_time, gc_alloc_count(diff)) val end end """ @timev This is a verbose version of the `@time` macro. It first prints the same information as `@time`, then any non-zero memory allocation counters, and then returns the value of the expression. See also [`@time`](@ref), [`@timed`](@ref), [`@elapsed`](@ref), and [`@allocated`](@ref). ```julia-repl julia> @timev rand(10^6); 0.001006 seconds (7 allocations: 7.630 MiB) elapsed time (ns): 1005567 bytes allocated: 8000256 pool allocs: 6 malloc() calls: 1 ``` """ macro timev(ex) quote local stats = gc_num() local elapsedtime = time_ns() local val = $(esc(ex)) elapsedtime = time_ns() - elapsedtime timev_print(elapsedtime, GC_Diff(gc_num(), stats)) val end end """ @elapsed A macro to evaluate an expression, discarding the resulting value, instead returning the number of seconds it took to execute as a floating-point number. See also [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref), and [`@allocated`](@ref). ```julia-repl julia> @elapsed sleep(0.3) 0.301391426 ``` """ macro elapsed(ex) quote local t0 = time_ns() local val = $(esc(ex)) (time_ns()-t0)/1e9 end end # measure bytes allocated without *most* contamination from compilation # Note: This reports a different value from the @time macros, because # it wraps the call in a function, however, this means that things # like: @allocated y = foo() # will not work correctly, because it will set y in the context of # the local function made by the macro, not the current function """ @allocated A macro to evaluate an expression, discarding the resulting value, instead returning the total number of bytes allocated during evaluation of the expression. Note: the expression is evaluated inside a local function, instead of the current context, in order to eliminate the effects of compilation, however, there still may be some allocations due to JIT compilation. This also makes the results inconsistent with the `@time` macros, which do not try to adjust for the effects of compilation. See also [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref), and [`@elapsed`](@ref). ```julia-repl julia> @allocated rand(10^6) 8000080 ``` """ macro allocated(ex) quote let local f function f() b0 = gc_bytes() $(esc(ex)) gc_bytes() - b0 end f() end end end """ @timed A macro to execute an expression, and return the value of the expression, elapsed time, total bytes allocated, garbage collection time, and an object with various memory allocation counters. See also [`@time`](@ref), [`@timev`](@ref), [`@elapsed`](@ref), and [`@allocated`](@ref). ```julia-repl julia> val, t, bytes, gctime, memallocs = @timed rand(10^6); julia> t 0.006634834 julia> bytes 8000256 julia> gctime 0.0055765 julia> fieldnames(typeof(memallocs)) 9-element Array{Symbol,1}: :allocd :malloc :realloc :poolalloc :bigalloc :freecall :total_time :pause :full_sweep julia> memallocs.total_time 5576500 ``` """ macro timed(ex) quote local stats = gc_num() local elapsedtime = time_ns() local val = $(esc(ex)) elapsedtime = time_ns() - elapsedtime local diff = GC_Diff(gc_num(), stats) val, elapsedtime/1e9, diff.allocd, diff.total_time/1e9, diff end end function fftw_vendor() if Base.libfftw_name in ("libmkl_rt", "mkl_rt") return :mkl else return :fftw end end ## printing with color ## function with_output_color(f::Function, color::Union{Int, Symbol}, io::IO, args...; bold::Bool = false) buf = IOBuffer() have_color && bold && print(buf, text_colors[:bold]) have_color && print(buf, get(text_colors, color, color_normal)) try f(IOContext(buf, io), args...) finally have_color && color != :nothing && print(buf, get(disable_text_style, color, text_colors[:default])) have_color && (bold || color == :bold) && print(buf, disable_text_style[:bold]) print(io, String(take!(buf))) end end """ print_with_color(color::Union{Symbol, Int}, [io], xs...; bold::Bool = false) Print `xs` in a color specified as a symbol. `color` may take any of the values $(Base.available_text_colors_docstring) or an integer between 0 and 255 inclusive. Note that not all terminals support 256 colors. If the keyword `bold` is given as `true`, the result will be printed in bold. """ print_with_color(color::Union{Int, Symbol}, io::IO, msg...; bold::Bool = false) = with_output_color(print, color, io, msg...; bold = bold) print_with_color(color::Union{Int, Symbol}, msg...; bold::Bool = false) = print_with_color(color, STDOUT, msg...; bold = bold) println_with_color(color::Union{Int, Symbol}, io::IO, msg...; bold::Bool = false) = with_output_color(println, color, io, msg...; bold = bold) println_with_color(color::Union{Int, Symbol}, msg...; bold::Bool = false) = println_with_color(color, STDOUT, msg...; bold = bold) ## warnings and messages ## const log_info_to = Dict{Tuple{Union{Module,Void},Union{Symbol,Void}},IO}() const log_warn_to = Dict{Tuple{Union{Module,Void},Union{Symbol,Void}},IO}() const log_error_to = Dict{Tuple{Union{Module,Void},Union{Symbol,Void}},IO}() function _redirect(io::IO, log_to::Dict, sf::StackTraces.StackFrame) isnull(sf.linfo) && return io mod = get(sf.linfo).def.module fun = sf.func if haskey(log_to, (mod,fun)) return log_to[(mod,fun)] elseif haskey(log_to, (mod,nothing)) return log_to[(mod,nothing)] elseif haskey(log_to, (nothing,nothing)) return log_to[(nothing,nothing)] else return io end end function _redirect(io::IO, log_to::Dict, fun::Symbol) clos = string("#",fun,"#") kw = string("kw##",fun) local sf break_next_frame = false for trace in backtrace() stack::Vector{StackFrame} = StackTraces.lookup(trace) filter!(frame -> !frame.from_c, stack) for frame in stack isnull(frame.linfo) && continue sf = frame break_next_frame && (@goto skip) get(frame.linfo).def.module == Base || continue sff = string(frame.func) if frame.func == fun || startswith(sff, clos) || startswith(sff, kw) break_next_frame = true end end end @label skip _redirect(io, log_to, sf) end @inline function redirect(io::IO, log_to::Dict, arg::Union{Symbol,StackTraces.StackFrame}) if isempty(log_to) return io else if length(log_to)==1 && haskey(log_to,(nothing,nothing)) return log_to[(nothing,nothing)] else return _redirect(io, log_to, arg) end end end """ logging(io [, m [, f]][; kind=:all]) logging([; kind=:all]) Stream output of informational, warning, and/or error messages to `io`, overriding what was otherwise specified. Optionally, divert stream only for module `m`, or specifically function `f` within `m`. `kind` can be `:all` (the default), `:info`, `:warn`, or `:error`. See `Base.log_{info,warn,error}_to` for the current set of redirections. Call `logging` with no arguments (or just the `kind`) to reset everything. """ function logging(io::IO, m::Union{Module,Void}=nothing, f::Union{Symbol,Void}=nothing; kind::Symbol=:all) (kind==:all || kind==:info) && (log_info_to[(m,f)] = io) (kind==:all || kind==:warn) && (log_warn_to[(m,f)] = io) (kind==:all || kind==:error) && (log_error_to[(m,f)] = io) nothing end function logging(; kind::Symbol=:all) (kind==:all || kind==:info) && empty!(log_info_to) (kind==:all || kind==:warn) && empty!(log_warn_to) (kind==:all || kind==:error) && empty!(log_error_to) nothing end """ info([io, ] msg..., [prefix="INFO: "]) Display an informational message. Argument `msg` is a string describing the information to be displayed. The `prefix` keyword argument can be used to override the default prepending of `msg`. ```jldoctest julia> info("hello world") INFO: hello world julia> info("hello world"; prefix="MY INFO: ") MY INFO: hello world ``` See also [`logging`](@ref). """ function info(io::IO, msg...; prefix="INFO: ") buf = IOBuffer() iob = redirect(IOContext(buf, io), log_info_to, :info) print_with_color(info_color(), iob, prefix; bold = true) println_with_color(info_color(), iob, chomp(string(msg...))) print(io, String(take!(buf))) return end info(msg...; prefix="INFO: ") = info(STDERR, msg..., prefix=prefix) # print a warning only once const have_warned = Set() warn_once(io::IO, msg...) = warn(io, msg..., once=true) warn_once(msg...) = warn(STDERR, msg..., once=true) """ warn([io, ] msg..., [prefix="WARNING: ", once=false, key=nothing, bt=nothing, filename=nothing, lineno::Int=0]) Display a warning. Argument `msg` is a string describing the warning to be displayed. Set `once` to true and specify a `key` to only display `msg` the first time `warn` is called. If `bt` is not `nothing` a backtrace is displayed. If `filename` is not `nothing` both it and `lineno` are displayed. See also [`logging`](@ref). """ function warn(io::IO, msg...; prefix="WARNING: ", once=false, key=nothing, bt=nothing, filename=nothing, lineno::Int=0) str = chomp(string(msg...)) if once if key === nothing key = str end (key in have_warned) && return push!(have_warned, key) end buf = IOBuffer() iob = redirect(IOContext(buf, io), log_warn_to, :warn) print_with_color(warn_color(), iob, prefix; bold = true) print_with_color(warn_color(), iob, str) if bt !== nothing show_backtrace(iob, bt) end if filename !== nothing print(iob, "\nwhile loading $filename, in expression starting on line $lineno") end println(iob) print(io, String(take!(buf))) return end """ warn(msg) Display a warning. Argument `msg` is a string describing the warning to be displayed. ```jldoctest julia> warn("Beep Beep") WARNING: Beep Beep ``` """ warn(msg...; kw...) = warn(STDERR, msg...; kw...) warn(io::IO, err::Exception; prefix="ERROR: ", kw...) = warn(io, sprint(showerror, err), prefix=prefix; kw...) warn(err::Exception; prefix="ERROR: ", kw...) = warn(STDERR, err, prefix=prefix; kw...) info(io::IO, err::Exception; prefix="ERROR: ", kw...) = info(io, sprint(showerror, err), prefix=prefix; kw...) info(err::Exception; prefix="ERROR: ", kw...) = info(STDERR, err, prefix=prefix; kw...) function julia_cmd(julia=joinpath(JULIA_HOME, julia_exename())) opts = JLOptions() cpu_target = unsafe_string(opts.cpu_target) image_file = unsafe_string(opts.image_file) compile = if opts.compile_enabled == 0 "no" elseif opts.compile_enabled == 2 "all" elseif opts.compile_enabled == 3 "min" else "yes" end depwarn = if opts.depwarn == 0 "no" elseif opts.depwarn == 2 "error" else "yes" end `$julia -C$cpu_target -J$image_file --compile=$compile --depwarn=$depwarn` end function julia_exename() if ccall(:jl_is_debugbuild, Cint, ()) == 0 return @static is_windows() ? "julia.exe" : "julia" else return @static is_windows() ? "julia-debug.exe" : "julia-debug" end end """ securezero!(o) `securezero!` fills the memory associated with an object `o` with zeros. Unlike `fill!(o,0)` and similar code, which might be optimized away by the compiler for objects about to be discarded, the `securezero!` function will always be called. """ function securezero! end @noinline securezero!(a::AbstractArray{<:Number}) = fill!(a, 0) securezero!(s::String) = unsafe_securezero!(pointer(s), sizeof(s)) @noinline unsafe_securezero!{T}(p::Ptr{T}, len::Integer=1) = ccall(:memset, Ptr{T}, (Ptr{T}, Cint, Csize_t), p, 0, len*sizeof(T)) unsafe_securezero!(p::Ptr{Void}, len::Integer=1) = Ptr{Void}(unsafe_securezero!(Ptr{UInt8}(p), len)) if is_windows() function getpass(prompt::AbstractString) print(prompt) flush(STDOUT) p = Vector{UInt8}(128) # mimic Unix getpass in ignoring more than 128-char passwords # (also avoids any potential memory copies arising from push!) try plen = 0 while true c = ccall(:_getch, UInt8, ()) if c == 0xff || c == UInt8('\n') || c == UInt8('\r') break # EOF or return elseif c == 0x00 || c == 0xe0 ccall(:_getch, UInt8, ()) # ignore function/arrow keys elseif c == UInt8('\b') && plen > 0 plen -= 1 # delete last character on backspace elseif !iscntrl(Char(c)) && plen < 128 p[plen += 1] = c end end return unsafe_string(pointer(p), plen) # use unsafe_string rather than String(p[1:plen]) # to be absolutely certain we never make an extra copy finally securezero!(p) end return "" end else getpass(prompt::AbstractString) = unsafe_string(ccall(:getpass, Cstring, (Cstring,), prompt)) end # Windows authentication prompt if is_windows() struct CREDUI_INFO cbSize::UInt32 parent::Ptr{Void} pszMessageText::Ptr{UInt16} pszCaptionText::Ptr{UInt16} banner::Ptr{Void} end const CREDUIWIN_GENERIC = 0x0001 const CREDUIWIN_IN_CRED_ONLY = 0x0020 const CREDUIWIN_ENUMERATE_CURRENT_USER = 0x0200 const CRED_PACK_GENERIC_CREDENTIALS = 0x0004 const ERROR_SUCCESS = 0x0000 const ERROR_CANCELLED = 0x04c7 function winprompt(message, caption, default_username; prompt_username = true) # Step 1: Create an encrypted username/password bundle that will be used to set # the default username (in theory could also provide a default password) credbuf = Array{UInt8,1}(1024) credbufsize = Ref{UInt32}(sizeof(credbuf)) succeeded = ccall((:CredPackAuthenticationBufferW, "credui.dll"), stdcall, Bool, (UInt32, Cwstring, Cwstring, Ptr{UInt8}, Ptr{UInt32}), CRED_PACK_GENERIC_CREDENTIALS, default_username, "", credbuf, credbufsize) @assert succeeded # Step 2: Create the actual dialog # 2.1: Set up the window messageArr = Base.cwstring(message) captionArr = Base.cwstring(caption) pfSave = Ref{Bool}(false) cred = Ref{CREDUI_INFO}(CREDUI_INFO(sizeof(CREDUI_INFO), C_NULL, pointer(messageArr), pointer(captionArr), C_NULL)) dwflags = CREDUIWIN_GENERIC | CREDUIWIN_ENUMERATE_CURRENT_USER if !prompt_username # Disable setting anything other than default_username dwflags |= CREDUIWIN_IN_CRED_ONLY end authPackage = Ref{Culong}(0) outbuf_data = Ref{Ptr{Void}}(C_NULL) outbuf_size = Ref{Culong}(0) # 2.2: Do the actual request code = ccall((:CredUIPromptForWindowsCredentialsW, "credui.dll"), stdcall, UInt32, (Ptr{CREDUI_INFO}, UInt32, Ptr{Culong}, Ptr{Void}, Culong, Ptr{Ptr{Void}}, Ptr{Culong}, Ptr{Bool}, UInt32), cred, 0, authPackage, credbuf, credbufsize[], outbuf_data, outbuf_size, pfSave, dwflags) # 2.3: If that failed for any reason other than the user canceling, error out. # If the user canceled, just return a nullable if code == ERROR_CANCELLED return Nullable{Tuple{String,String}}() elseif code != ERROR_SUCCESS error(Base.Libc.FormatMessage(code)) end # Step 3: Convert encrypted credentials back to plain text passbuf = Array{UInt16,1}(1024) passlen = Ref{UInt32}(length(passbuf)) usernamebuf = Array{UInt16,1}(1024) usernamelen = Ref{UInt32}(length(usernamebuf)) # Need valid buffers for domain, even though we don't care dummybuf = Array{UInt16,1}(1024) succeeded = ccall((:CredUnPackAuthenticationBufferW, "credui.dll"), Bool, (UInt32, Ptr{Void}, UInt32, Ptr{UInt16}, Ptr{UInt32}, Ptr{UInt16}, Ptr{UInt32}, Ptr{UInt16}, Ptr{UInt32}), 0, outbuf_data[], outbuf_size[], usernamebuf, usernamelen, dummybuf, Ref{UInt32}(1024), passbuf, passlen) if !succeeded error(Base.Libc.FormatMessage()) end # Step 4: Free the encrypted buffer # ccall(:SecureZeroMemory, Ptr{Void}, (Ptr{Void}, Csize_t), outbuf_data[], outbuf_size[]) - not an actual function unsafe_securezero!(outbuf_data[], outbuf_size[]) ccall((:CoTaskMemFree, "ole32.dll"), Void, (Ptr{Void},), outbuf_data[]) # Done. passbuf_ = passbuf[1:passlen[]-1] result = Nullable((String(transcode(UInt8, usernamebuf[1:usernamelen[]-1])), String(transcode(UInt8, passbuf_)))) securezero!(passbuf_) securezero!(passbuf) return result end end """ crc32c(data, crc::UInt32=0x00000000) Compute the CRC-32c checksum of the given `data`, which can be an `Array{UInt8}` or a `String`. Optionally, you can pass a starting `crc` integer to be mixed in with the checksum. (Technically, a little-endian checksum is computed.) """ function crc32c end crc32c(a::Union{Array{UInt8},String}, crc::UInt32=0x00000000) = ccall(:jl_crc32c, UInt32, (UInt32, Ptr{UInt8}, Csize_t), crc, a, sizeof(a)) """ @kwdef typedef This is a helper macro that automatically defines a keyword-based constructor for the type declared in the expression `typedef`, which must be a `struct` or `mutable struct` expression. The default argument is supplied by declaring fields of the form `field::T = default`. If no default is provided then the default is provided by the `kwdef_val(T)` function. ```julia @kwdef struct Foo a::Cint # implied default Cint(0) b::Cint = 1 # specified default z::Cstring # implied default Cstring(C_NULL) y::Bar # implied default Bar() end ``` """ macro kwdef(expr) expr = macroexpand(expr) # to expand @static T = expr.args[2] params_ex = Expr(:parameters) call_ex = Expr(:call, T) _kwdef!(expr.args[3], params_ex, call_ex) quote Base.@__doc__($(esc(expr))) $(esc(Expr(:call,T,params_ex))) = $(esc(call_ex)) end end # @kwdef helper function # mutates arguments inplace function _kwdef!(blk, params_ex, call_ex) for i in eachindex(blk.args) ei = blk.args[i] isa(ei, Expr) || continue if ei.head == :(=) # var::Typ = defexpr dec = ei.args[1] # var::Typ var = dec.args[1] # var def = ei.args[2] # defexpr push!(params_ex.args, Expr(:kw, var, def)) push!(call_ex.args, var) blk.args[i] = dec elseif ei.head == :(::) dec = ei # var::Typ var = dec.args[1] # var def = :(Base.kwdef_val($(ei.args[2]))) push!(params_ex.args, Expr(:kw, var, def)) push!(call_ex.args, dec.args[1]) elseif ei.head == :block # can arise with use of @static inside type decl _kwdef!(ei, params_ex, call_ex) end end blk end """ kwdef_val(T) The default value for a type for use with the `@kwdef` macro. Returns: - null pointer for pointer types (`Ptr{T}`, `Cstring`, `Cwstring`) - zero for integer types - no-argument constructor calls (e.g. `T()`) for all other types """ function kwdef_val end kwdef_val(::Type{Ptr{T}}) where {T} = Ptr{T}(C_NULL) kwdef_val(::Type{Cstring}) = Cstring(C_NULL) kwdef_val(::Type{Cwstring}) = Cwstring(C_NULL) kwdef_val(::Type{T}) where {T<:Integer} = zero(T) kwdef_val(::Type{T}) where {T} = T()