• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37599

16 Aug 2023 01:53AM UTC coverage: 86.427% (-1.1%) from 87.526%
#37599

push

local

web-flow
Change heap-size-hint in test processes to total memory (#50922)

It seems this is causing macos to hang because the shown free memory is
generally very small.

xref: JuliaLang/julia#50673

73086 of 84564 relevant lines covered (86.43%)

32342958.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

23.21
/stdlib/InteractiveUtils/src/macros.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
# macro wrappers for various reflection functions
4

5
import Base: typesof, insert!, replace_ref_begin_end!, infer_effects
6

7
separate_kwargs(args...; kwargs...) = (args, values(kwargs))
700✔
8

9
"""
10
Transform a dot expression into one where each argument has been replaced by a
11
variable "xj" (with j an integer from 1 to the returned i).
12
The list `args` contains the original arguments that have been replaced.
13
"""
14
function recursive_dotcalls!(ex, args, i=1)
×
15
    if !(ex isa Expr) || ((ex.head !== :. || !(ex.args[2] isa Expr)) &&
×
16
                          (ex.head !== :call || string(ex.args[1])[1] != '.'))
17
        newarg = Symbol('x', i)
×
18
        if Meta.isexpr(ex, :...)
×
19
            push!(args, only(ex.args))
×
20
            return Expr(:..., newarg), i+1
×
21
        else
22
            push!(args, ex)
×
23
            return newarg, i+1
×
24
        end
25
    end
26
    (start, branches) = ex.head === :. ? (1, ex.args[2].args) : (2, ex.args)
×
27
    length_branches = length(branches)::Int
×
28
    for j in start:length_branches
×
29
        branch, i = recursive_dotcalls!(branches[j], args, i)
×
30
        branches[j] = branch
×
31
    end
×
32
    return ex, i
×
33
end
34

35
function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
273✔
36
    if Meta.isexpr(ex0, :ref)
273✔
37
        ex0 = replace_ref_begin_end!(ex0)
×
38
    end
39
    if isa(ex0, Expr)
145✔
40
        if ex0.head === :do && Meta.isexpr(get(ex0.args, 1, nothing), :call)
145✔
41
            if length(ex0.args) != 2
×
42
                return Expr(:call, :error, "ill-formed do call")
×
43
            end
44
            i = findlast(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args[1].args)
×
45
            args = copy(ex0.args[1].args)
×
46
            insert!(args, (isnothing(i) ? 2 : 1+i::Int), ex0.args[2])
×
47
            ex0 = Expr(:call, args...)
×
48
        end
49
        if ex0.head === :. || (ex0.head === :call && ex0.args[1] !== :.. && string(ex0.args[1])[1] == '.')
290✔
50
            codemacro = startswith(string(fcn), "code_")
×
51
            if codemacro && (ex0.head === :call || ex0.args[2] isa Expr)
×
52
                # Manually wrap a dot call in a function
53
                args = Any[]
×
54
                ex, i = recursive_dotcalls!(copy(ex0), args)
×
55
                xargs = [Symbol('x', j) for j in 1:i-1]
×
56
                dotfuncname = gensym("dotfunction")
×
57
                dotfuncdef = Expr(:local, Expr(:(=), Expr(:call, dotfuncname, xargs...), ex))
×
58
                return quote
×
59
                    $(esc(dotfuncdef))
60
                    local args = $typesof($(map(esc, args)...))
61
                    $(fcn)($(esc(dotfuncname)), args; $(kws...))
62
                end
63
            elseif !codemacro
×
64
                fully_qualified_symbol = true # of the form A.B.C.D
×
65
                ex1 = ex0
×
66
                while ex1 isa Expr && ex1.head === :.
×
67
                    fully_qualified_symbol = (length(ex1.args) == 2 &&
×
68
                                              ex1.args[2] isa QuoteNode &&
69
                                              ex1.args[2].value isa Symbol)
70
                    fully_qualified_symbol || break
×
71
                    ex1 = ex1.args[1]
×
72
                end
×
73
                fully_qualified_symbol &= ex1 isa Symbol
×
74
                if fully_qualified_symbol
×
75
                    return quote
×
76
                        local arg1 = $(esc(ex0.args[1]))
77
                        if isa(arg1, Module)
78
                            $(if string(fcn) == "which"
79
                                  :(which(arg1, $(ex0.args[2])))
×
80
                              else
81
                                  :(error("expression is not a function call"))
×
82
                              end)
83
                        else
84
                            local args = $typesof($(map(esc, ex0.args)...))
85
                            $(fcn)(Base.getproperty, args)
86
                        end
87
                    end
88
                else
89
                    return Expr(:call, :error, "dot expressions are not lowered to "
×
90
                                * "a single function call, so @$fcn cannot analyze "
91
                                * "them. You may want to use Meta.@lower to identify "
92
                                * "which function call to target.")
93
                end
94
            end
95
        end
96
        if any(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args)
911✔
97
            return quote
124✔
98
                local arg1 = $(esc(ex0.args[1]))
99
                local args, kwargs = $separate_kwargs($(map(esc, ex0.args[2:end])...))
100
                $(fcn)(Core.kwcall,
101
                       Tuple{typeof(kwargs), Core.Typeof(arg1), map(Core.Typeof, args)...};
102
                       $(kws...))
103
            end
104
        elseif ex0.head === :call
21✔
105
            return Expr(:call, fcn, esc(ex0.args[1]),
21✔
106
                        Expr(:call, typesof, map(esc, ex0.args[2:end])...),
107
                        kws...)
108
        elseif ex0.head === :(=) && length(ex0.args) == 2
×
109
            lhs, rhs = ex0.args
×
110
            if isa(lhs, Expr)
×
111
                if lhs.head === :(.)
×
112
                    return Expr(:call, fcn, Base.setproperty!,
×
113
                                Expr(:call, typesof, map(esc, lhs.args)..., esc(rhs)), kws...)
114
                elseif lhs.head === :ref
×
115
                    return Expr(:call, fcn, Base.setindex!,
×
116
                                Expr(:call, typesof, esc(lhs.args[1]), esc(rhs), map(esc, lhs.args[2:end])...), kws...)
117
                end
118
            end
119
        elseif ex0.head === :vcat || ex0.head === :typed_vcat
×
120
            if ex0.head === :vcat
×
121
                f, hf = Base.vcat, Base.hvcat
×
122
                args = ex0.args
×
123
            else
124
                f, hf = Base.typed_vcat, Base.typed_hvcat
×
125
                args = ex0.args[2:end]
×
126
            end
127
            if any(a->isa(a,Expr) && a.head === :row, args)
×
128
                rows = Any[ (isa(x,Expr) && x.head === :row ? x.args : Any[x]) for x in args ]
×
129
                lens = map(length, rows)
×
130
                return Expr(:call, fcn, hf,
×
131
                            Expr(:call, typesof,
132
                                 (ex0.head === :vcat ? [] : Any[esc(ex0.args[1])])...,
133
                                 Expr(:tuple, lens...),
134
                                 map(esc, vcat(rows...))...), kws...)
135
            else
136
                return Expr(:call, fcn, f,
×
137
                            Expr(:call, typesof, map(esc, ex0.args)...), kws...)
138
            end
139
        else
140
            for (head, f) in (:ref => Base.getindex, :hcat => Base.hcat, :(.) => Base.getproperty, :vect => Base.vect, Symbol("'") => Base.adjoint, :typed_hcat => Base.typed_hcat, :string => string)
×
141
                if ex0.head === head
×
142
                    return Expr(:call, fcn, f,
×
143
                                Expr(:call, typesof, map(esc, ex0.args)...), kws...)
144
                end
145
            end
×
146
        end
147
    end
148
    if isa(ex0, Expr) && ex0.head === :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions*
×
149
        return Expr(:call, fcn, esc(ex0.args[1]), Tuple{#=__source__=#LineNumberNode, #=__module__=#Module, Any[ Core.Typeof(a) for a in ex0.args[3:end] ]...}, kws...)
×
150
    end
151

152
    ex = Meta.lower(__module__, ex0)
×
153
    if !isa(ex, Expr)
×
154
        return Expr(:call, :error, "expression is not a function call or symbol")
×
155
    end
156

157
    exret = Expr(:none)
×
158
    if ex.head === :call
×
159
        if any(e->(isa(e, Expr) && e.head === :(...)), ex0.args) &&
×
160
            (ex.args[1] === GlobalRef(Core,:_apply_iterate) ||
161
             ex.args[1] === GlobalRef(Base,:_apply_iterate))
162
            # check for splatting
163
            exret = Expr(:call, ex.args[2], fcn,
×
164
                        Expr(:tuple, esc(ex.args[3]),
165
                            Expr(:call, typesof, map(esc, ex.args[4:end])...)))
166
        else
167
            exret = Expr(:call, fcn, esc(ex.args[1]),
×
168
                         Expr(:call, typesof, map(esc, ex.args[2:end])...), kws...)
169
        end
170
    end
171
    if ex.head === :thunk || exret.head === :none
×
172
        exret = Expr(:call, :error, "expression is not a function call, "
×
173
                                  * "or is too complex for @$fcn to analyze; "
174
                                  * "break it down to simpler parts if possible. "
175
                                  * "In some cases, you may want to use Meta.@lower.")
176
    end
177
    return exret
×
178
end
179

180
"""
181
Same behaviour as `gen_call_with_extracted_types` except that keyword arguments
182
of the form "foo=bar" are passed on to the called function as well.
183
The keyword arguments must be given before the mandatory argument.
184
"""
185
function gen_call_with_extracted_types_and_kwargs(__module__, fcn, ex0)
17✔
186
    kws = Expr[]
17✔
187
    arg = ex0[end] # Mandatory argument
17✔
188
    for i in 1:length(ex0)-1
17✔
189
        x = ex0[i]
×
190
        if x isa Expr && x.head === :(=) # Keyword given of the form "foo=bar"
×
191
            if length(x.args) != 2
×
192
                return Expr(:call, :error, "Invalid keyword argument: $x")
×
193
            end
194
            push!(kws, Expr(:kw, esc(x.args[1]), esc(x.args[2])))
×
195
        else
196
            return Expr(:call, :error, "@$fcn expects only one non-keyword argument")
×
197
        end
198
    end
×
199
    return gen_call_with_extracted_types(__module__, fcn, arg, kws)
17✔
200
end
201

202
for fname in [:which, :less, :edit, :functionloc]
203
    @eval begin
204
        macro ($fname)(ex0)
5✔
205
            gen_call_with_extracted_types(__module__, $(Expr(:quote, fname)), ex0)
5✔
206
        end
207
    end
208
end
209

210
macro which(ex0::Symbol)
1✔
211
    ex0 = QuoteNode(ex0)
1✔
212
    return :(which($__module__, $ex0))
1✔
213
end
214

215
for fname in [:code_warntype, :code_llvm, :code_native, :infer_effects]
216
    @eval begin
217
        macro ($fname)(ex0...)
218
            gen_call_with_extracted_types_and_kwargs(__module__, $(Expr(:quote, fname)), ex0)
219
        end
220
    end
221
end
222

223
macro code_typed(ex0...)
2✔
224
    thecall = gen_call_with_extracted_types_and_kwargs(__module__, :code_typed, ex0)
2✔
225
    quote
2✔
226
        local results = $thecall
227
        length(results) == 1 ? results[1] : results
228
    end
229
end
230

231
macro code_lowered(ex0...)
9✔
232
    thecall = gen_call_with_extracted_types_and_kwargs(__module__, :code_lowered, ex0)
9✔
233
    quote
9✔
234
        local results = $thecall
235
        length(results) == 1 ? results[1] : results
236
    end
237
end
238

239
macro time_imports(ex)
240
    quote
241
        try
242
            Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1)
243
            $(esc(ex))
244
        finally
245
            Base.Threads.atomic_sub!(Base.TIMING_IMPORTS, 1)
246
        end
247
    end
248
end
249

250
"""
251
    @functionloc
252

253
Applied to a function or macro call, it evaluates the arguments to the specified call, and
254
returns a tuple `(filename,line)` giving the location for the method that would be called for those arguments.
255
It calls out to the [`functionloc`](@ref) function.
256
"""
257
:@functionloc
258

259
"""
260
    @which
261

262
Applied to a function or macro call, it evaluates the arguments to the specified call, and
263
returns the `Method` object for the method that would be called for those arguments. Applied
264
to a variable, it returns the module in which the variable was bound. It calls out to the
265
[`which`](@ref) function.
266

267
See also: [`@less`](@ref), [`@edit`](@ref).
268
"""
269
:@which
270

271
"""
272
    @less
273

274
Evaluates the arguments to the function or macro call, determines their types, and calls the [`less`](@ref)
275
function on the resulting expression.
276

277
See also: [`@edit`](@ref), [`@which`](@ref), [`@code_lowered`](@ref).
278
"""
279
:@less
280

281
"""
282
    @edit
283

284
Evaluates the arguments to the function or macro call, determines their types, and calls the [`edit`](@ref)
285
function on the resulting expression.
286

287
See also: [`@less`](@ref), [`@which`](@ref).
288
"""
289
:@edit
290

291
"""
292
    @code_typed
293

294
Evaluates the arguments to the function or macro call, determines their types, and calls
295
[`code_typed`](@ref) on the resulting expression. Use the optional argument `optimize` with
296

297
    @code_typed optimize=true foo(x)
298

299
to control whether additional optimizations, such as inlining, are also applied.
300
"""
301
:@code_typed
302

303
"""
304
    @code_lowered
305

306
Evaluates the arguments to the function or macro call, determines their types, and calls
307
[`code_lowered`](@ref) on the resulting expression.
308
"""
309
:@code_lowered
310

311
"""
312
    @code_warntype
313

314
Evaluates the arguments to the function or macro call, determines their types, and calls
315
[`code_warntype`](@ref) on the resulting expression.
316
"""
317
:@code_warntype
318

319
"""
320
    @code_llvm
321

322
Evaluates the arguments to the function or macro call, determines their types, and calls
323
[`code_llvm`](@ref) on the resulting expression.
324
Set the optional keyword arguments `raw`, `dump_module`, `debuginfo`, `optimize`
325
by putting them and their value before the function call, like this:
326

327
    @code_llvm raw=true dump_module=true debuginfo=:default f(x)
328
    @code_llvm optimize=false f(x)
329

330
`optimize` controls whether additional optimizations, such as inlining, are also applied.
331
`raw` makes all metadata and dbg.* calls visible.
332
`debuginfo` may be one of `:source` (default) or `:none`,  to specify the verbosity of code comments.
333
`dump_module` prints the entire module that encapsulates the function.
334
"""
335
:@code_llvm
336

337
"""
338
    @code_native
339

340
Evaluates the arguments to the function or macro call, determines their types, and calls
341
[`code_native`](@ref) on the resulting expression.
342

343
Set any of the optional keyword arguments `syntax`, `debuginfo`, `binary` or `dump_module`
344
by putting it before the function call, like this:
345

346
    @code_native syntax=:intel debuginfo=:default binary=true dump_module=false f(x)
347

348
* Set assembly syntax by setting `syntax` to `:intel` (default) for Intel syntax or `:att` for AT&T syntax.
349
* Specify verbosity of code comments by setting `debuginfo` to `:source` (default) or `:none`.
350
* If `binary` is `true`, also print the binary machine code for each instruction precedented by an abbreviated address.
351
* If `dump_module` is `false`, do not print metadata such as rodata or directives.
352

353
See also: [`code_native`](@ref), [`@code_llvm`](@ref), [`@code_typed`](@ref) and [`@code_lowered`](@ref)
354
"""
355
:@code_native
356

357
"""
358
    @time_imports
359

360
A macro to execute an expression and produce a report of any time spent importing packages and their
361
dependencies. Any compilation time will be reported as a percentage, and how much of which was recompilation, if any.
362

363
One line is printed per package or package extension. The duration shown is the time to import that package itself, not including the time to load any of its dependencies.
364

365
On Julia 1.9+ [package extensions](@ref man-extensions) will show as Parent → Extension.
366

367
!!! note
368
    During the load process a package sequentially imports all of its dependencies, not just its direct dependencies.
369

370
```julia-repl
371
julia> @time_imports using CSV
372
     50.7 ms  Parsers 17.52% compilation time
373
      0.2 ms  DataValueInterfaces
374
      1.6 ms  DataAPI
375
      0.1 ms  IteratorInterfaceExtensions
376
      0.1 ms  TableTraits
377
     17.5 ms  Tables
378
     26.8 ms  PooledArrays
379
    193.7 ms  SentinelArrays 75.12% compilation time
380
      8.6 ms  InlineStrings
381
     20.3 ms  WeakRefStrings
382
      2.0 ms  TranscodingStreams
383
      1.4 ms  Zlib_jll
384
      1.8 ms  CodecZlib
385
      0.8 ms  Compat
386
     13.1 ms  FilePathsBase 28.39% compilation time
387
   1681.2 ms  CSV 92.40% compilation time
388
```
389

390
!!! compat "Julia 1.8"
391
    This macro requires at least Julia 1.8
392

393
"""
394
:@time_imports
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc