diff --git a/base/client.jl b/base/client.jl index ec75882521ba57..9fda3ccfc60584 100644 --- a/base/client.jl +++ b/base/client.jl @@ -419,6 +419,7 @@ function _start() end if repl + eval(Main, :(using InteractiveUtils)) if !isa(STDIN,TTY) # note: currently IOStream is used for file STDIN if isa(STDIN,File) || isa(STDIN,IOStream) diff --git a/base/clipboard.jl b/base/clipboard.jl new file mode 100644 index 00000000000000..42c753376baaf6 --- /dev/null +++ b/base/clipboard.jl @@ -0,0 +1,113 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# clipboard copy and paste + +if Sys.isapple() + function clipboard(x) + open(pipeline(`pbcopy`, stderr=STDERR), "w") do io + print(io, x) + end + end + clipboard() = read(`pbpaste`, String) + +elseif Sys.islinux() || Sys.KERNEL === :FreeBSD + _clipboardcmd = nothing + const _clipboardcmds = Dict( + :copy => Dict( + :xsel => Sys.islinux() ? + `xsel --nodetach --input --clipboard` : `xsel -c`, + :xclip => `xclip -silent -in -selection clipboard`, + ), + :paste => Dict( + :xsel => Sys.islinux() ? + `xsel --nodetach --output --clipboard` : `xsel -p`, + :xclip => `xclip -quiet -out -selection clipboard`, + ) + ) + function clipboardcmd() + global _clipboardcmd + _clipboardcmd !== nothing && return _clipboardcmd + for cmd in (:xclip, :xsel) + success(pipeline(`which $cmd`, DevNull)) && return _clipboardcmd = cmd + end + pkgs = @static if Sys.islinux() + "xsel or xclip" + elseif Sys.KERNEL === :FreeBSD + "x11/xsel or x11/xclip" + end + error("no clipboard command found, please install $pkgs") + end + function clipboard(x) + c = clipboardcmd() + cmd = get(_clipboardcmds[:copy], c, nothing) + if cmd === nothing + error("unexpected clipboard command: $c") + end + open(pipeline(cmd, stderr=STDERR), "w") do io + print(io, x) + end + end + function clipboard() + c = clipboardcmd() + cmd = get(_clipboardcmds[:paste], c, nothing) + if cmd === nothing + error("unexpected clipboard command: $c") + end + read(pipeline(cmd, stderr=STDERR), String) + end + +elseif Sys.iswindows() + # TODO: these functions leak memory and memory locks if they throw an error + function clipboard(x::AbstractString) + if containsnul(x) + throw(ArgumentError("Windows clipboard strings cannot contain NUL character")) + end + systemerror(:OpenClipboard, 0==ccall((:OpenClipboard, "user32"), stdcall, Cint, (Ptr{Cvoid},), C_NULL)) + systemerror(:EmptyClipboard, 0==ccall((:EmptyClipboard, "user32"), stdcall, Cint, ())) + x_u16 = cwstring(x) + # copy data to locked, allocated space + p = ccall((:GlobalAlloc, "kernel32"), stdcall, Ptr{UInt16}, (UInt16, Int32), 2, sizeof(x_u16)) + systemerror(:GlobalAlloc, p==C_NULL) + plock = ccall((:GlobalLock, "kernel32"), stdcall, Ptr{UInt16}, (Ptr{UInt16},), p) + systemerror(:GlobalLock, plock==C_NULL) + ccall(:memcpy, Ptr{UInt16}, (Ptr{UInt16},Ptr{UInt16},Int), plock, x_u16, sizeof(x_u16)) + systemerror(:GlobalUnlock, 0==ccall((:GlobalUnlock, "kernel32"), stdcall, Cint, (Ptr{Cvoid},), plock)) + pdata = ccall((:SetClipboardData, "user32"), stdcall, Ptr{UInt16}, (UInt32, Ptr{UInt16}), 13, p) + systemerror(:SetClipboardData, pdata!=p) + ccall((:CloseClipboard, "user32"), stdcall, Cvoid, ()) + end + clipboard(x) = clipboard(sprint(print, x)::String) + function clipboard() + systemerror(:OpenClipboard, 0==ccall((:OpenClipboard, "user32"), stdcall, Cint, (Ptr{Cvoid},), C_NULL)) + pdata = ccall((:GetClipboardData, "user32"), stdcall, Ptr{UInt16}, (UInt32,), 13) + systemerror(:SetClipboardData, pdata==C_NULL) + systemerror(:CloseClipboard, 0==ccall((:CloseClipboard, "user32"), stdcall, Cint, ())) + plock = ccall((:GlobalLock, "kernel32"), stdcall, Ptr{UInt16}, (Ptr{UInt16},), pdata) + systemerror(:GlobalLock, plock==C_NULL) + # find NUL terminator (0x0000 16-bit code unit) + len = 0 + while unsafe_load(plock, len+1) != 0; len += 1; end + # get Vector{UInt16}, transcode data to UTF-8, make a String of it + s = transcode(String, unsafe_wrap(Array, plock, len)) + systemerror(:GlobalUnlock, 0==ccall((:GlobalUnlock, "kernel32"), stdcall, Cint, (Ptr{UInt16},), plock)) + return s + end + +else + clipboard(x="") = error("`clipboard` function not implemented for $(Sys.KERNEL)") +end + + +""" + clipboard(x) + +Send a printed form of `x` to the operating system clipboard ("copy"). +""" +clipboard(x) + +""" + clipboard() -> AbstractString + +Return a string with the contents of the operating system clipboard ("paste"). +""" +clipboard() diff --git a/base/deprecated.jl b/base/deprecated.jl index 8940e23ca99044..2fa5d08f3923a7 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -203,10 +203,6 @@ next(p::Union{Process, ProcessChain}, i::Int) = (getindex(p, i), i + 1) return i == 1 ? getfield(p, p.openstream) : p end -# PR #21974 -@deprecate versioninfo(verbose::Bool) versioninfo(verbose=verbose) -@deprecate versioninfo(io::IO, verbose::Bool) versioninfo(io, verbose=verbose) - # also remove all support machinery in src for current_module when removing this deprecation # and make Base.include an error _current_module() = ccall(:jl_get_current_module, Ref{Module}, ()) @@ -744,14 +740,6 @@ Broadcast.dotview(A::AbstractArray{<:AbstractArray}, args::Integer...) = getinde nothing end -@deprecate whos(io::IO, m::Module, pat::Regex) show(io, varinfo(m, pat)) -@deprecate whos(io::IO, m::Module) show(io, varinfo(m)) -@deprecate whos(io::IO) show(io, varinfo()) -@deprecate whos(m::Module, pat::Regex) varinfo(m, pat) -@deprecate whos(m::Module) varinfo(m) -@deprecate whos(pat::Regex) varinfo(pat) -@deprecate whos() varinfo() - # indexing with A[true] will throw an argument error in the future function to_index(i::Bool) depwarn("indexing with Bool values is deprecated. Convert the index to an integer first with `Int(i)`.", (:getindex, :setindex!, :view)) @@ -1382,7 +1370,6 @@ export readandwrite @deprecate indmax argmax @deprecate runtests(tests, ncores; kw...) runtests(tests; ncores = ncores, kw...) false -@deprecate methodswith(typ, supertypes) methodswith(typ, supertypes = supertypes) @deprecate code_lowered(f, types, generated) code_lowered(f, types, generated = generated) # PR 25458 @@ -1402,8 +1389,6 @@ end @deprecate Timer(callback, delay, repeat) Time(callback, delay, interval = repeat) @deprecate names(m, all) names(m, all = all) @deprecate names(m, all, imported) names(m, all = all, imported = imported) -@deprecate code_native(io, f, types, syntax) code_native(io, f, types, syntax = syntax) -@deprecate code_native(f, types, syntax) code_native(f, types, syntax = syntax) @deprecate eachmatch(re, str, overlap) eachmatch(re, str, overlap = overlap) @deprecate matchall(re, str, overlap) matchall(re, str, overlap = overlap) @deprecate chop(s, head) chop(s, head = head) @@ -1418,6 +1403,8 @@ end @deprecate print_with_color(color, args...; kwargs...) printstyled(args...; kwargs..., color=color) +@deprecate which(s::Symbol) which(Main, s) + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index d5d56b4e0f3ffa..f5895c6b99abbf 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -64,7 +64,7 @@ import Base.Meta: quot, isexpr import Base: Callable, with_output_color import ..CoreDocs: lazy_iterpolate -export doc, apropos +export doc # Basic API / Storage diff --git a/base/download.jl b/base/download.jl new file mode 100644 index 00000000000000..be22a4aad19f36 --- /dev/null +++ b/base/download.jl @@ -0,0 +1,59 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# file downloading + +downloadcmd = nothing +if Sys.iswindows() + downloadcmd = :powershell + function download(url::AbstractString, filename::AbstractString) + ps = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" + tls12 = "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" + client = "New-Object System.Net.Webclient" + # in the following we escape ' with '' (see https://ss64.com/ps/syntax-esc.html) + downloadfile = "($client).DownloadFile('$(replace(url, "'" => "''"))', '$(replace(filename, "'" => "''"))')" + run(`$ps -NoProfile -Command "$tls12; $downloadfile"`) + filename + end +else + function download(url::AbstractString, filename::AbstractString) + global downloadcmd + if downloadcmd === nothing + for checkcmd in (:curl, :wget, :fetch) + if success(pipeline(`which $checkcmd`, DevNull)) + downloadcmd = checkcmd + break + end + end + end + if downloadcmd == :wget + try + run(`wget -O $filename $url`) + catch + rm(filename) # wget always creates a file + rethrow() + end + elseif downloadcmd == :curl + run(`curl -g -L -f -o $filename $url`) + elseif downloadcmd == :fetch + run(`fetch -f $filename $url`) + else + error("no download agent available; install curl, wget, or fetch") + end + filename + end +end +function download(url::AbstractString) + filename = tempname() + download(url, filename) +end + +""" + download(url::AbstractString, [localfile::AbstractString]) + +Download a file from the given url, optionally renaming it to the given local file name. +Note that this function relies on the availability of external tools such as `curl`, `wget` +or `fetch` to download the file and is provided for convenience. For production use or +situations in which more options are needed, please use a package that provides the desired +functionality instead. +""" +download(url, filename) diff --git a/base/errorshow.jl b/base/errorshow.jl index de9f53fffb37f5..2cb16e9079ee3c 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -149,6 +149,8 @@ function showerror(io::IO, ex::InexactError) print(io, "InexactError: ", ex.func, '(', ex.T, ", ", ex.val, ')') end +typesof(args...) = Tuple{Any[ Core.Typeof(a) for a in args ]...} + function showerror(io::IO, ex::MethodError) # ex.args is a tuple type if it was thrown from `invoke` and is # a tuple of the arguments otherwise. diff --git a/base/exports.jl b/base/exports.jl index bf3f588f983cf4..e1db865293733d 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -761,7 +761,6 @@ export promote, promote_rule, promote_type, - subtypes, instances, supertype, typeintersect, @@ -777,26 +776,17 @@ export parse, # help and reflection - apropos, - edit, code_typed, - code_warntype, code_lowered, - code_llvm, - code_native, fullname, functionloc, isconst, isinteractive, - less, hasmethod, methods, - methodswith, nameof, parentmodule, names, - varinfo, - versioninfo, which, @isdefined, @@ -1033,17 +1023,6 @@ export @elapsed, @allocated, - # reflection - @which, - @edit, - @functionloc, - @less, - @code_typed, - @code_warntype, - @code_lowered, - @code_llvm, - @code_native, - # tasks @schedule, @sync, diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl deleted file mode 100644 index d185ef17a1bfd8..00000000000000 --- a/base/interactiveutil.jl +++ /dev/null @@ -1,754 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# editing files - -""" - editor() - -Determine the editor to use when running functions like `edit`. Return an `Array` compatible -for use within backticks. You can change the editor by setting `JULIA_EDITOR`, `VISUAL` or -`EDITOR` as an environment variable. -""" -function editor() - if Sys.iswindows() || Sys.isapple() - default_editor = "open" - elseif isfile("/etc/alternatives/editor") - default_editor = "/etc/alternatives/editor" - else - default_editor = "emacs" - end - # Note: the editor path can include spaces (if escaped) and flags. - args = shell_split(get(ENV,"JULIA_EDITOR", get(ENV,"VISUAL", get(ENV,"EDITOR", default_editor)))) - isempty(args) && error("editor is empty") - return args -end - -""" - edit(path::AbstractString, line::Integer=0) - -Edit a file or directory optionally providing a line number to edit the file at. -Return to the `julia` prompt when you quit the editor. The editor can be changed -by setting `JULIA_EDITOR`, `VISUAL` or `EDITOR` as an environment variable. -""" -function edit(path::AbstractString, line::Integer=0) - command = editor() - name = basename(first(command)) - if endswith(path, ".jl") - f = find_source_file(path) - f !== nothing && (path = f) - end - background = true - line_unsupported = false - if startswith(name, "vim.") || name == "vi" || name == "vim" || name == "nvim" || - name == "mvim" || name == "nano" || - name == "emacs" && any(c -> c in ["-nw", "--no-window-system" ], command) || - name == "emacsclient" && any(c -> c in ["-nw", "-t", "-tty"], command) - cmd = line != 0 ? `$command +$line $path` : `$command $path` - background = false - elseif startswith(name, "emacs") || name == "gedit" || startswith(name, "gvim") - cmd = line != 0 ? `$command +$line $path` : `$command $path` - elseif name == "textmate" || name == "mate" || name == "kate" - cmd = line != 0 ? `$command $path -l $line` : `$command $path` - elseif startswith(name, "subl") || startswith(name, "atom") - cmd = line != 0 ? `$command $path:$line` : `$command $path` - elseif name == "code" || (Sys.iswindows() && uppercase(name) == "CODE.EXE") - cmd = line != 0 ? `$command -g $path:$line` : `$command -g $path` - elseif startswith(name, "notepad++") - cmd = line != 0 ? `$command $path -n$line` : `$command $path` - elseif Sys.isapple() && name == "open" - cmd = `open -t $path` - line_unsupported = true - else - cmd = `$command $path` - background = false - line_unsupported = true - end - - if Sys.iswindows() && name == "open" - @static Sys.iswindows() && # don't emit this ccall on other platforms - systemerror(:edit, ccall((:ShellExecuteW, "shell32"), stdcall, Int, - (Ptr{Cvoid}, Cwstring, Cwstring, Ptr{Cvoid}, Ptr{Cvoid}, Cint), - C_NULL, "open", path, C_NULL, C_NULL, 10) ≤ 32) - elseif background - spawn(pipeline(cmd, stderr=STDERR)) - else - run(cmd) - end - line != 0 && line_unsupported && println("Unknown editor: no line number information passed.\nThe method is defined at line $line.") - - nothing -end - -""" - edit(function, [types]) - -Edit the definition of a function, optionally specifying a tuple of types to -indicate which method to edit. The editor can be changed by setting `JULIA_EDITOR`, -`VISUAL` or `EDITOR` as an environment variable. -""" -edit(f) = edit(functionloc(f)...) -edit(f, @nospecialize t) = edit(functionloc(f,t)...) -edit(file, line::Integer) = error("could not find source file for function") - -# terminal pager - -if Sys.iswindows() - function less(file::AbstractString, line::Integer) - pager = shell_split(get(ENV, "PAGER", "more")) - g = pager[1] == "more" ? "" : "g" - run(Cmd(`$pager +$(line)$(g) \"$file\"`, windows_verbatim = true)) - end -else - function less(file::AbstractString, line::Integer) - pager = shell_split(get(ENV, "PAGER", "less")) - run(`$pager +$(line)g $file`) - end -end - -""" - less(file::AbstractString, [line::Integer]) - -Show a file using the default pager, optionally providing a starting line number. Returns to -the `julia` prompt when you quit the pager. -""" -less(file::AbstractString) = less(file, 1) - -""" - less(function, [types]) - -Show the definition of a function using the default pager, optionally specifying a tuple of -types to indicate which method to see. -""" -less(f) = less(functionloc(f)...) -less(f, @nospecialize t) = less(functionloc(f,t)...) -less(file, line::Integer) = error("could not find source file for function") - -# clipboard copy and paste - -if Sys.isapple() - function clipboard(x) - open(pipeline(`pbcopy`, stderr=STDERR), "w") do io - print(io, x) - end - end - clipboard() = read(`pbpaste`, String) - -elseif Sys.islinux() || Sys.KERNEL === :FreeBSD - _clipboardcmd = nothing - const _clipboardcmds = Dict( - :copy => Dict( - :xsel => Sys.islinux() ? - `xsel --nodetach --input --clipboard` : `xsel -c`, - :xclip => `xclip -silent -in -selection clipboard`, - ), - :paste => Dict( - :xsel => Sys.islinux() ? - `xsel --nodetach --output --clipboard` : `xsel -p`, - :xclip => `xclip -quiet -out -selection clipboard`, - ) - ) - function clipboardcmd() - global _clipboardcmd - _clipboardcmd !== nothing && return _clipboardcmd - for cmd in (:xclip, :xsel) - success(pipeline(`which $cmd`, DevNull)) && return _clipboardcmd = cmd - end - pkgs = @static if Sys.islinux() - "xsel or xclip" - elseif Sys.KERNEL === :FreeBSD - "x11/xsel or x11/xclip" - end - error("no clipboard command found, please install $pkgs") - end - function clipboard(x) - c = clipboardcmd() - cmd = get(_clipboardcmds[:copy], c, nothing) - if cmd === nothing - error("unexpected clipboard command: $c") - end - open(pipeline(cmd, stderr=STDERR), "w") do io - print(io, x) - end - end - function clipboard() - c = clipboardcmd() - cmd = get(_clipboardcmds[:paste], c, nothing) - if cmd === nothing - error("unexpected clipboard command: $c") - end - read(pipeline(cmd, stderr=STDERR), String) - end - -elseif Sys.iswindows() - # TODO: these functions leak memory and memory locks if they throw an error - function clipboard(x::AbstractString) - if containsnul(x) - throw(ArgumentError("Windows clipboard strings cannot contain NUL character")) - end - systemerror(:OpenClipboard, 0==ccall((:OpenClipboard, "user32"), stdcall, Cint, (Ptr{Cvoid},), C_NULL)) - systemerror(:EmptyClipboard, 0==ccall((:EmptyClipboard, "user32"), stdcall, Cint, ())) - x_u16 = cwstring(x) - # copy data to locked, allocated space - p = ccall((:GlobalAlloc, "kernel32"), stdcall, Ptr{UInt16}, (UInt16, Int32), 2, sizeof(x_u16)) - systemerror(:GlobalAlloc, p==C_NULL) - plock = ccall((:GlobalLock, "kernel32"), stdcall, Ptr{UInt16}, (Ptr{UInt16},), p) - systemerror(:GlobalLock, plock==C_NULL) - ccall(:memcpy, Ptr{UInt16}, (Ptr{UInt16},Ptr{UInt16},Int), plock, x_u16, sizeof(x_u16)) - systemerror(:GlobalUnlock, 0==ccall((:GlobalUnlock, "kernel32"), stdcall, Cint, (Ptr{Cvoid},), plock)) - pdata = ccall((:SetClipboardData, "user32"), stdcall, Ptr{UInt16}, (UInt32, Ptr{UInt16}), 13, p) - systemerror(:SetClipboardData, pdata!=p) - ccall((:CloseClipboard, "user32"), stdcall, Cvoid, ()) - end - clipboard(x) = clipboard(sprint(print, x)::String) - function clipboard() - systemerror(:OpenClipboard, 0==ccall((:OpenClipboard, "user32"), stdcall, Cint, (Ptr{Cvoid},), C_NULL)) - pdata = ccall((:GetClipboardData, "user32"), stdcall, Ptr{UInt16}, (UInt32,), 13) - systemerror(:SetClipboardData, pdata==C_NULL) - systemerror(:CloseClipboard, 0==ccall((:CloseClipboard, "user32"), stdcall, Cint, ())) - plock = ccall((:GlobalLock, "kernel32"), stdcall, Ptr{UInt16}, (Ptr{UInt16},), pdata) - systemerror(:GlobalLock, plock==C_NULL) - # find NUL terminator (0x0000 16-bit code unit) - len = 0 - while unsafe_load(plock, len+1) != 0; len += 1; end - # get Vector{UInt16}, transcode data to UTF-8, make a String of it - s = transcode(String, unsafe_wrap(Array, plock, len)) - systemerror(:GlobalUnlock, 0==ccall((:GlobalUnlock, "kernel32"), stdcall, Cint, (Ptr{UInt16},), plock)) - return s - end - -else - clipboard(x="") = error("`clipboard` function not implemented for $(Sys.KERNEL)") -end - - -""" - clipboard(x) - -Send a printed form of `x` to the operating system clipboard ("copy"). -""" -clipboard(x) - -""" - clipboard() -> AbstractString - -Return a string with the contents of the operating system clipboard ("paste"). -""" -clipboard() - - -# system information - -# used by sysinfo.jl -function _show_cpuinfo(io::IO, info::Sys.CPUinfo, header::Bool=true, prefix::AbstractString=" ") - tck = Sys.SC_CLK_TCK - if header - println(io, info.model, ": ") - print(io, " "^length(prefix)) - if tck > 0 - Printf.@printf(io, " %5s %9s %9s %9s %9s %9s\n", - "speed", "user", "nice", "sys", "idle", "irq") - else - Printf.@printf(io, " %5s %9s %9s %9s %9s %9s ticks\n", - "speed", "user", "nice", "sys", "idle", "irq") - end - end - print(io, prefix) - if tck > 0 - Printf.@printf(io, "%5d MHz %9d s %9d s %9d s %9d s %9d s", - info.speed, info.cpu_times!user / tck, info.cpu_times!nice / tck, - info.cpu_times!sys / tck, info.cpu_times!idle / tck, info.cpu_times!irq / tck) - else - Printf.@printf(io, "%5d MHz %9d %9d %9d %9d %9d ticks", - info.speed, info.cpu_times!user, info.cpu_times!nice, - info.cpu_times!sys, info.cpu_times!idle, info.cpu_times!irq) - end -end - - -""" - versioninfo(io::IO=STDOUT; verbose::Bool=false, packages::Bool=false) - -Print information about the version of Julia in use. The output is -controlled with boolean keyword arguments: - -- `packages`: print information about installed packages -- `verbose`: print all additional information -""" -function versioninfo(io::IO=STDOUT; verbose::Bool=false, packages::Bool=false) - println(io, "Julia Version $VERSION") - if !isempty(GIT_VERSION_INFO.commit_short) - println(io, "Commit $(GIT_VERSION_INFO.commit_short) ($(GIT_VERSION_INFO.date_string))") - end - if ccall(:jl_is_debugbuild, Cint, ())!=0 - println(io, "DEBUG build") - end - println(io, "Platform Info:") - println(io, " OS: ", Sys.iswindows() ? "Windows" : Sys.isapple() ? - "macOS" : Sys.KERNEL, " (", Sys.MACHINE, ")") - - if verbose - lsb = "" - if Sys.islinux() - try lsb = readchomp(pipeline(`lsb_release -ds`, stderr=DevNull)) end - end - if Sys.iswindows() - try lsb = strip(read(`$(ENV["COMSPEC"]) /c ver`, String)) end - end - if !isempty(lsb) - println(io, " ", lsb) - end - if Sys.isunix() - println(io, " uname: ", readchomp(`uname -mprsv`)) - end - end - - if verbose - cpuio = IOBuffer() # print cpu_summary with correct alignment - Sys.cpu_summary(cpuio) - for (i, line) in enumerate(split(String(take!(cpuio)), "\n")) - prefix = i == 1 ? " CPU: " : " " - println(io, prefix, line) - end - else - cpu = Sys.cpu_info() - println(io, " CPU: ", cpu[1].model) - end - - if verbose - println(io, " Memory: $(Sys.total_memory()/2^30) GB ($(Sys.free_memory()/2^20) MB free)") - try println(io, " Uptime: $(Sys.uptime()) sec") end - print(io, " Load Avg: ") - print_matrix(io, Sys.loadavg()') - println(io) - end - println(io, " WORD_SIZE: ", Sys.WORD_SIZE) - println(io, " LIBM: ",libm_name) - println(io, " LLVM: libLLVM-",libllvm_version," (", Sys.JIT, ", ", Sys.CPU_NAME, ")") - - println(io, "Environment:") - for (k,v) in ENV - if contains(String(k), r"JULIA") - println(io, " $(k) = $(v)") - end - end - if verbose - for (k,v) in ENV - if contains(String(k), r"PATH|FLAG|^TERM$|HOME") - println(io, " $(k) = $(v)") - end - end - end - if packages || verbose - println(io, "Packages:") - println(io, " Package Directory: ", Pkg.dir()) - print(io, " Package Status:") - if isdir(Pkg.dir()) - println(io, "") - Pkg.status(io) - else - println(io, " no packages installed") - end - end -end - -# displaying type-ambiguity warnings -""" - code_warntype([io::IO], f, types) - -Prints lowered and type-inferred ASTs for the methods matching the given generic function -and type signature to `io` which defaults to `STDOUT`. The ASTs are annotated in such a way -as to cause "non-leaf" types to be emphasized (if color is available, displayed in red). -This serves as a warning of potential type instability. Not all non-leaf types are particularly -problematic for performance, so the results need to be used judiciously. -In particular, unions containing either [`missing`](@ref) or [`nothing`](@ref) are displayed in yellow, since -these are often intentional. -See [`@code_warntype`](@ref man-code-warntype) for more information. -""" -function code_warntype(io::IO, f, @nospecialize(t)) - function slots_used(ci, slotnames) - used = falses(length(slotnames)) - scan_exprs!(used, ci.code) - return used - end - - function scan_exprs!(used, exprs) - for ex in exprs - if isa(ex, Slot) - used[ex.id] = true - elseif isa(ex, Expr) - scan_exprs!(used, ex.args) - end - end - end - - emph_io = IOContext(io, :TYPEEMPHASIZE => true) - for (src, rettype) in code_typed(f, t) - println(emph_io, "Variables:") - slotnames = sourceinfo_slotnames(src) - used_slotids = slots_used(src, slotnames) - for i = 1:length(slotnames) - if used_slotids[i] - print(emph_io, " ", slotnames[i]) - if isa(src.slottypes, Array) - show_expr_type(emph_io, src.slottypes[i], true) - end - print(emph_io, '\n') - elseif !('#' in slotnames[i] || '@' in slotnames[i]) - print(emph_io, " ", slotnames[i], "\n") - end - end - print(emph_io, "\nBody:\n ") - body = Expr(:body) - body.args = src.code - body.typ = rettype - # Fix slot names and types in function body - show_unquoted(IOContext(emph_io, :SOURCEINFO => src, :SOURCE_SLOTNAMES => slotnames), - body, 2) - print(emph_io, '\n') - end - nothing -end -code_warntype(f, @nospecialize(t)) = code_warntype(STDOUT, f, t) - -typesof(args...) = Tuple{Any[ Core.Typeof(a) for a in args ]...} - -function gen_call_with_extracted_types(__module__, fcn, ex0) - if isa(ex0, Expr) - if any(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args) - # remove keyword args, but call the kwfunc - args = filter(a->!(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args) - return quote - local arg1 = $(esc(args[1])) - $(fcn)(Core.kwfunc(arg1), - Tuple{Any, Core.Typeof(arg1), - $(typesof)($(map(esc, args[2:end])...)).parameters...}) - end - elseif ex0.head == :call - return Expr(:call, fcn, esc(ex0.args[1]), - Expr(:call, typesof, map(esc, ex0.args[2:end])...)) - elseif ex0.head == :(.) - return Expr(:call, fcn, :getproperty, - Expr(:call, typesof, map(esc, ex0.args)...)) - elseif ex0.head == :(=) && length(ex0.args) == 2 && ex0.args[1].head == :(.) - return Expr(:call, fcn, :(setproperty!), - Expr(:call, typesof, map(esc, [ex0.args[1].args..., ex0.args[2]])...)) - end - end - if isa(ex0, Expr) && ex0.head == :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions* - return Expr(:call, fcn, esc(ex0.args[1]), Tuple{#=__source__=#LineNumberNode, #=__module__=#Module, Any[ Core.Typeof(a) for a in ex0.args[3:end] ]...}) - end - ex = Meta.lower(__module__, ex0) - exret = Expr(:none) - if !isa(ex, Expr) - exret = Expr(:call, :error, "expression is not a function call or symbol") - elseif ex.head == :call - if any(e->(isa(e, Expr) && e.head==:(...)), ex0.args) && - (ex.args[1] === GlobalRef(Core,:_apply) || - ex.args[1] === GlobalRef(Base,:_apply)) - # check for splatting - exret = Expr(:call, ex.args[1], fcn, - Expr(:tuple, esc(ex.args[2]), - Expr(:call, typesof, map(esc, ex.args[3:end])...))) - else - exret = Expr(:call, fcn, esc(ex.args[1]), - Expr(:call, typesof, map(esc, ex.args[2:end])...)) - end - elseif ex.head == :body - a1 = ex.args[1] - if isa(a1, Expr) && a1.head == :call - a11 = a1.args[1] - if a11 == :setindex! - exret = Expr(:call, fcn, a11, - Expr(:call, typesof, map(esc, a1.args[2:end])...)) - end - end - end - if ex.head == :thunk || exret.head == :none - exret = Expr(:call, :error, "expression is not a function call, " - * "or is too complex for @$fcn to analyze; " - * "break it down to simpler parts if possible") - end - return exret -end - -for fname in [:which, :less, :edit, :functionloc, :code_warntype, - :code_llvm, :code_llvm_raw, :code_native] - @eval begin - macro ($fname)(ex0) - gen_call_with_extracted_types(__module__, $(Expr(:quote, fname)), ex0) - end - end -end -macro which(ex0::Symbol) - ex0 = QuoteNode(ex0) - return :(which_module($__module__, $ex0)) -end - -for fname in [:code_typed, :code_lowered] - @eval begin - macro ($fname)(ex0) - thecall = gen_call_with_extracted_types(__module__, $(Expr(:quote, fname)), ex0) - quote - results = $thecall - length(results) == 1 ? results[1] : results - end - end - end -end - -""" - @which - -Applied to a function or macro call, it evaluates the arguments to the specified call, and -returns the `Method` object for the method that would be called for those arguments. Applied -to a variable, it returns the module in which the variable was bound. It calls out to the -`which` function. -""" -:@which - -""" - @less - -Evaluates the arguments to the function or macro call, determines their types, and calls the `less` -function on the resulting expression. -""" -:@less - -""" - @edit - -Evaluates the arguments to the function or macro call, determines their types, and calls the `edit` -function on the resulting expression. -""" -:@edit - -""" - @functionloc - -Applied to a function or macro call, it evaluates the arguments to the specified call, and -returns a tuple `(filename,line)` giving the location for the method that would be called for those arguments. -It calls out to the `functionloc` function. -""" -:@functionloc - -""" - @code_typed - -Evaluates the arguments to the function or macro call, determines their types, and calls -[`code_typed`](@ref) on the resulting expression. -""" -:@code_typed - -""" - @code_warntype - -Evaluates the arguments to the function or macro call, determines their types, and calls -[`code_warntype`](@ref) on the resulting expression. -""" -:@code_warntype - -""" - @code_lowered - -Evaluates the arguments to the function or macro call, determines their types, and calls -[`code_lowered`](@ref) on the resulting expression. -""" -:@code_lowered - -""" - @code_llvm - -Evaluates the arguments to the function or macro call, determines their types, and calls -[`code_llvm`](@ref) on the resulting expression. -""" -:@code_llvm - -""" - @code_native - -Evaluates the arguments to the function or macro call, determines their types, and calls -[`code_native`](@ref) on the resulting expression. -""" -:@code_native - -function type_close_enough(@nospecialize(x), @nospecialize(t)) - x == t && return true - # TODO: handle UnionAll properly - return (isa(x, DataType) && isa(t, DataType) && x.name === t.name && x <: t) || - (isa(x, Union) && isa(t, DataType) && (type_close_enough(x.a, t) || type_close_enough(x.b, t))) -end - -# `methodswith` -- shows a list of methods using the type given -""" - methodswith(typ[, module or function]; supertypes::Bool=false]) - -Return an array of methods with an argument of type `typ`. - -The optional second argument restricts the search to a particular module or function -(the default is all top-level modules). - -If keyword `supertypes` is `true`, also return arguments with a parent type of `typ`, -excluding type `Any`. -""" -function methodswith(t::Type, f::Function, meths = Method[]; supertypes::Bool=false) - for d in methods(f) - if any(function (x) - let x = rewrap_unionall(x, d.sig) - (type_close_enough(x, t) || - (supertypes ? (t <: x && (!isa(x,TypeVar) || x.ub != Any)) : - (isa(x,TypeVar) && x.ub != Any && t == x.ub)) && - x != Any) - end - end, - unwrap_unionall(d.sig).parameters) - push!(meths, d) - end - end - return meths -end - -function _methodswith(t::Type, m::Module, supertypes::Bool) - meths = Method[] - for nm in names(m) - if isdefined(m, nm) - f = getfield(m, nm) - if isa(f, Function) - methodswith(t, f, meths; supertypes = supertypes) - end - end - end - return unique(meths) -end - -methodswith(t::Type, m::Module; supertypes::Bool=false) = _methodswith(t, m, supertypes) - -function methodswith(t::Type; supertypes::Bool=false) - meths = Method[] - for mod in loaded_modules_array() - append!(meths, _methodswith(t, mod, supertypes)) - end - return unique(meths) -end - -# file downloading - -downloadcmd = nothing -if Sys.iswindows() - downloadcmd = :powershell - function download(url::AbstractString, filename::AbstractString) - ps = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" - tls12 = "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" - client = "New-Object System.Net.Webclient" - # in the following we escape ' with '' (see https://ss64.com/ps/syntax-esc.html) - downloadfile = "($client).DownloadFile('$(replace(url, "'" => "''"))', '$(replace(filename, "'" => "''"))')" - run(`$ps -NoProfile -Command "$tls12; $downloadfile"`) - filename - end -else - function download(url::AbstractString, filename::AbstractString) - global downloadcmd - if downloadcmd === nothing - for checkcmd in (:curl, :wget, :fetch) - if success(pipeline(`which $checkcmd`, DevNull)) - downloadcmd = checkcmd - break - end - end - end - if downloadcmd == :wget - try - run(`wget -O $filename $url`) - catch - rm(filename) # wget always creates a file - rethrow() - end - elseif downloadcmd == :curl - run(`curl -g -L -f -o $filename $url`) - elseif downloadcmd == :fetch - run(`fetch -f $filename $url`) - else - error("no download agent available; install curl, wget, or fetch") - end - filename - end -end -function download(url::AbstractString) - filename = tempname() - download(url, filename) -end - -""" - download(url::AbstractString, [localfile::AbstractString]) - -Download a file from the given url, optionally renaming it to the given local file name. -Note that this function relies on the availability of external tools such as `curl`, `wget` -or `fetch` to download the file and is provided for convenience. For production use or -situations in which more options are needed, please use a package that provides the desired -functionality instead. -""" -download(url, filename) - -# testing - -""" - Base.runtests(tests=["all"]; ncores=ceil(Int, Sys.CPU_CORES / 2), - exit_on_error=false, [seed]) - -Run the Julia unit tests listed in `tests`, which can be either a string or an array of -strings, using `ncores` processors. If `exit_on_error` is `false`, when one test -fails, all remaining tests in other files will still be run; they are otherwise discarded, -when `exit_on_error == true`. -If a seed is provided via the keyword argument, it is used to seed the -global RNG in the context where the tests are run; otherwise the seed is chosen randomly. -""" -function runtests(tests = ["all"]; ncores = ceil(Int, Sys.CPU_CORES / 2), - exit_on_error=false, - seed::Union{BitInteger,Nothing}=nothing) - if isa(tests,AbstractString) - tests = split(tests) - end - exit_on_error && push!(tests, "--exit-on-error") - seed != nothing && push!(tests, "--seed=0x$(hex(seed % UInt128))") # cast to UInt128 to avoid a minus sign - ENV2 = copy(ENV) - ENV2["JULIA_CPU_CORES"] = "$ncores" - try - run(setenv(`$(julia_cmd()) $(joinpath(Sys.BINDIR, - Base.DATAROOTDIR, "julia", "test", "runtests.jl")) $tests`, ENV2)) - catch - buf = PipeBuffer() - versioninfo(buf) - error("A test has failed. Please submit a bug report (https://github.com/JuliaLang/julia/issues)\n" * - "including error messages above and the output of versioninfo():\n$(read(buf, String))") - end -end - -# varinfo - -const MARKDOWN_MODULE_REF = Ref{Module}() - -""" - varinfo(m::Module=Main, pattern::Regex=r"") - -Return a markdown table giving information about exported global variables in a module, optionally restricted -to those matching `pattern`. - -The memory consumption estimate is an approximate lower bound on the size of the internal structure of the object. -""" -function varinfo(m::Module=Main, pattern::Regex=r"") - rows = - Any[ let value = getfield(m, v) - Any[string(v), - (any(x -> x === value, (Base, Main, Core)) ? "" : format_bytes(summarysize(value))), - summary(value)] - end - for v in sort!(names(m)) if isdefined(m, v) && contains(string(v), pattern) ] - - pushfirst!(rows, Any["name", "size", "summary"]) - - if !isassigned(MARKDOWN_MODULE_REF) - MARKDOWN_MODULE_REF[] = root_module(Base, :Markdown) - end - Markdown = MARKDOWN_MODULE_REF[] - return Markdown.MD(Any[Markdown.Table(rows, Symbol[:l, :r, :l])]) -end -varinfo(pat::Regex) = varinfo(Main, pat) diff --git a/base/methodshow.jl b/base/methodshow.jl index 379670383ff611..c19b85595b0af0 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -301,7 +301,7 @@ end show(io::IO, mime::MIME"text/html", mt::MethodTable) = show(io, mime, MethodList(mt)) -# pretty-printing of AbstractVector{Method} for output of methodswith: +# pretty-printing of AbstractVector{Method} function show(io::IO, mime::MIME"text/plain", mt::AbstractVector{Method}) resize!(LAST_SHOWN_LINE_INFOS, 0) first = true diff --git a/base/precompile.jl b/base/precompile.jl index fa50391dd8af6a..6615a46ec5fb9b 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -608,8 +608,6 @@ precompile(Tuple{typeof(Base._setindex!), Base.Dict{Union{DataType, typeof(Type) precompile(Tuple{typeof(Base.setindex!), Base.Dict{Union{DataType, typeof(Type)}, Nothing}, Nothing, DataType}) precompile(Tuple{typeof(Base.setindex!), Base.Dict{Union{DataType, typeof(Type)}, Nothing}, Nothing, typeof(Type)}) precompile(Tuple{typeof(Base.rehash!), Base.Dict{Union{DataType, typeof(Type)}, Nothing}, Int64}) -precompile(Tuple{typeof(Base._subtypes), Module, DataType, Base.Set{Union{DataType, typeof(Type)}}, Base.Set{Module}}) -precompile(Tuple{typeof(Base._subtypes), Module, DataType}) precompile(Tuple{typeof(Base.isempty), Array{Int64, 1}}) precompile(Tuple{typeof(Base.Iterators.zip), Array{Int64, 1}, SimpleVector}) precompile(Tuple{typeof(Base.Iterators.zip), Array{Symbol, 1}, SimpleVector}) diff --git a/base/reflection.jl b/base/reflection.jl index 9548de63fa846e..919685de67f0d2 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -584,74 +584,6 @@ julia> instances(Color) """ function instances end -# subtypes -function _subtypes(m::Module, x::Union{DataType,UnionAll}, - sts=Set{Union{DataType,UnionAll}}(), visited=Set{Module}()) - push!(visited, m) - xt = unwrap_unionall(x) - if !isa(xt, DataType) - return sts - end - xt = xt::DataType - for s in names(m, all = true) - if isdefined(m, s) && !isdeprecated(m, s) - t = getfield(m, s) - if isa(t, DataType) - t = t::DataType - if t.name.name === s && supertype(t).name == xt.name - ti = typeintersect(t, x) - ti != Bottom && push!(sts, ti) - end - elseif isa(t, UnionAll) - t = t::UnionAll - tt = unwrap_unionall(t) - isa(tt, DataType) || continue - tt = tt::DataType - if tt.name.name === s && supertype(tt).name == xt.name - ti = typeintersect(t, x) - ti != Bottom && push!(sts, ti) - end - elseif isa(t, Module) - t = t::Module - in(t, visited) || _subtypes(t, x, sts, visited) - end - end - end - return sts -end - -function _subtypes_in(mods::Array, x::Union{DataType,UnionAll}) - if !isabstracttype(x) - # Fast path - return Union{DataType,UnionAll}[] - end - sts = Set{Union{DataType,UnionAll}}() - visited = Set{Module}() - for m in mods - _subtypes(m, x, sts, visited) - end - return sort!(collect(sts), by=string) -end - -subtypes(m::Module, x::Union{DataType,UnionAll}) = _subtypes_in([m], x) - -""" - subtypes(T::DataType) - -Return a list of immediate subtypes of DataType `T`. Note that all currently loaded subtypes -are included, including those not visible in the current module. - -# Examples -```jldoctest -julia> subtypes(Integer) -3-element Array{Union{DataType, UnionAll},1}: - Bool - Signed - Unsigned -``` -""" -subtypes(x::Union{DataType,UnionAll}) = _subtypes_in(loaded_modules_array(), x) - function to_tuple_type(@nospecialize(t)) @_pure_meta if isa(t,Tuple) || isa(t,AbstractArray) || isa(t,SimpleVector) @@ -858,79 +790,6 @@ struct CodegenParams module_setup, module_activation, raise_exception) end -# Printing code representations in IR and assembly -function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrapper::Bool, - strip_ir_metadata::Bool, dump_module::Bool, syntax::Symbol=:att, - optimize::Bool=true, params::CodegenParams=CodegenParams()) - ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") - if isa(f, Core.Builtin) - throw(ArgumentError("argument is not a generic function")) - end - # get the MethodInstance for the method match - world = typemax(UInt) - meth = which(f, t) - t = to_tuple_type(t) - tt = signature_type(f, t) - (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), tt, meth.sig)::SimpleVector - meth = func_for_method_checked(meth, ti) - linfo = ccall(:jl_specializations_get_linfo, Ref{Core.MethodInstance}, (Any, Any, Any, UInt), meth, ti, env, world) - # get the code for it - return _dump_function_linfo(linfo, world, native, wrapper, strip_ir_metadata, dump_module, syntax, optimize, params) -end - -function _dump_function_linfo(linfo::Core.MethodInstance, world::UInt, native::Bool, wrapper::Bool, - strip_ir_metadata::Bool, dump_module::Bool, syntax::Symbol=:att, - optimize::Bool=true, params::CodegenParams=CodegenParams()) - if syntax != :att && syntax != :intel - throw(ArgumentError("'syntax' must be either :intel or :att")) - end - if native - llvmf = ccall(:jl_get_llvmf_decl, Ptr{Cvoid}, (Any, UInt, Bool, CodegenParams), linfo, world, wrapper, params) - else - llvmf = ccall(:jl_get_llvmf_defn, Ptr{Cvoid}, (Any, UInt, Bool, Bool, CodegenParams), linfo, world, wrapper, optimize, params) - end - if llvmf == C_NULL - error("could not compile the specified method") - end - - if native - str = ccall(:jl_dump_function_asm, Ref{String}, - (Ptr{Cvoid}, Cint, Ptr{UInt8}), llvmf, 0, syntax) - else - str = ccall(:jl_dump_function_ir, Ref{String}, - (Ptr{Cvoid}, Bool, Bool), llvmf, strip_ir_metadata, dump_module) - end - - # TODO: use jl_is_cacheable_sig instead of isdispatchtuple - isdispatchtuple(linfo.specTypes) || (str = "; WARNING: This code may not match what actually runs.\n" * str) - return str -end - -""" - code_llvm([io=STDOUT,], f, types) - -Prints the LLVM bitcodes generated for running the method matching the given generic -function and type signature to `io`. - -All metadata and dbg.* calls are removed from the printed bitcode. Use code_llvm_raw for the full IR. -""" -code_llvm(io::IO, @nospecialize(f), @nospecialize(types=Tuple), strip_ir_metadata=true, dump_module=false) = - print(io, _dump_function(f, types, false, false, strip_ir_metadata, dump_module)) -code_llvm(@nospecialize(f), @nospecialize(types=Tuple)) = code_llvm(STDOUT, f, types) -code_llvm_raw(@nospecialize(f), @nospecialize(types=Tuple)) = code_llvm(STDOUT, f, types, false) - -""" - code_native([io=STDOUT,], f, types; syntax = :att) - -Prints the native assembly instructions generated for running the method matching the given -generic function and type signature to `io`. -Switch assembly syntax using `syntax` symbol parameter set to `:att` for AT&T syntax or `:intel` for Intel syntax. -""" -code_native(io::IO, @nospecialize(f), @nospecialize(types=Tuple); syntax::Symbol = :att) = - print(io, _dump_function(f, types, true, false, false, false, syntax)) -code_native(@nospecialize(f), @nospecialize(types=Tuple); syntax::Symbol = :att) = code_native(STDOUT, f, types, syntax = syntax) -code_native(::IO, ::Any, ::Symbol) = error("illegal code_native call") # resolve ambiguous call - # give a decent error message if we try to instantiate a staged function on non-leaf types function func_for_method_checked(m::Method, @nospecialize types) if isdefined(m, :generator) && !isdispatchtuple(types) @@ -1004,13 +863,11 @@ function which(@nospecialize(f), @nospecialize(t)) end """ - which(symbol) + which(module, symbol) -Return the module in which the binding for the variable referenced by `symbol` in module `Main` was created. +Return the module in which the binding for the variable referenced by `symbol` in `module` was created. """ -which(s::Symbol) = which_module(Main, s) -# TODO: making this a method of which() causes a strange error -function which_module(m::Module, s::Symbol) +function which(m::Module, s::Symbol) if !isdefined(m, s) error("\"$s\" is not defined in module $m") end diff --git a/base/show.jl b/base/show.jl index 651850d0bb9ddb..ac9251bc027ac8 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1710,78 +1710,8 @@ function dump(io::IO, x::DataType, n::Int, indent) nothing end -# dumptype is for displaying abstract type hierarchies, -# based on Jameson Nash's examples/typetree.jl -function dumptype(io::IO, @nospecialize(x), n::Int, indent) - print(io, x) - n == 0 && return # too deeply nested - isa(x, DataType) && x.abstract && dumpsubtypes(io, x, Main, n, indent) - nothing -end - -directsubtype(a::DataType, b::DataType) = supertype(a).name === b.name -directsubtype(a::UnionAll, b::DataType) = directsubtype(a.body, b) -directsubtype(a::Union, b::DataType) = directsubtype(a.a, b) || directsubtype(a.b, b) -# Fallback to handle TypeVar's -directsubtype(a, b::DataType) = false -function dumpsubtypes(io::IO, x::DataType, m::Module, n::Int, indent) - for s in names(m, all = true) - if isdefined(m, s) && !isdeprecated(m, s) - t = getfield(m, s) - if t === x || t === m - continue - elseif isa(t, Module) && nameof(t) === s && parentmodule(t) === m - # recurse into primary module bindings - dumpsubtypes(io, x, t, n, indent) - elseif isa(t, UnionAll) && directsubtype(t::UnionAll, x) - dt = unwrap_unionall(t) - println(io) - if isa(dt, DataType) && dt.name.wrapper === t - # primary type binding - print(io, indent, " ") - dumptype(io, dt, n - 1, string(indent, " ")) - else - # aliases to types - print(io, indent, " ", m, ".", s, "{") - tvar_io::IOContext = io - tp = t - while true - show(tvar_io, tp.var) - tvar_io = IOContext(tvar_io, :unionall_env => tp.var) - tp = tp.body - if isa(tp, UnionAll) - print(io, ", ") - else - print(io, "} = ") - break - end - end - show(tvar_io, tp) - end - elseif isa(t, Union) && directsubtype(t::Union, x) - println(io) - print(io, indent, " ", m, ".", s, " = ", t) - elseif isa(t, DataType) && directsubtype(t::DataType, x) - println(io) - if t.name.module !== m || t.name.name != s - # aliases to types - print(io, indent, " ", m, ".", s, " = ") - show(io, t) - else - # primary type binding - print(io, indent, " ") - dumptype(io, t, n - 1, string(indent, " ")) - end - end - end - end - nothing -end - - const DUMP_DEFAULT_MAXDEPTH = 8 -# For abstract types, use _dumptype only if it's a form that will be called -# interactively. + dump(io::IO, arg; maxdepth=DUMP_DEFAULT_MAXDEPTH) = (dump(io, arg, maxdepth, ""); println(io)) """ diff --git a/base/sysimg.jl b/base/sysimg.jl index 917efbdcfeca70..05bf8aead3c831 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -415,9 +415,10 @@ using .Enums # concurrency and parallelism include("channels.jl") -# utilities - timing, help, edit +# utilities include("deepcopy.jl") -include("interactiveutil.jl") +include("clipboard.jl") +include("download.jl") include("summarysize.jl") include("errorshow.jl") include("i18n.jl") @@ -834,6 +835,27 @@ Base.require(Base, :Markdown) @deprecate_stdlib normalize_string Unicode true @deprecate_stdlib graphemes Unicode true @deprecate_stdlib is_assigned_char Unicode true + + @deprecate_stdlib whos InteractiveUtils true + @deprecate_stdlib subtypes InteractiveUtils true + @deprecate_stdlib apropos InteractiveUtils true + @deprecate_stdlib edit InteractiveUtils true + @deprecate_stdlib less InteractiveUtils true + @deprecate_stdlib code_llvm InteractiveUtils true + @deprecate_stdlib code_native InteractiveUtils true + @deprecate_stdlib code_warntype InteractiveUtils true + @deprecate_stdlib methodswith InteractiveUtils true + @deprecate_stdlib varinfo InteractiveUtils true + @deprecate_stdlib versioninfo InteractiveUtils true + @eval @deprecate_stdlib $(Symbol("@which")) InteractiveUtils true + @eval @deprecate_stdlib $(Symbol("@edit")) InteractiveUtils true + @eval @deprecate_stdlib $(Symbol("@less")) InteractiveUtils true + @eval @deprecate_stdlib $(Symbol("@functionloc")) InteractiveUtils true + @eval @deprecate_stdlib $(Symbol("@code_typed")) InteractiveUtils true + @eval @deprecate_stdlib $(Symbol("@code_warntype")) InteractiveUtils true + @eval @deprecate_stdlib $(Symbol("@code_lowered")) InteractiveUtils true + @eval @deprecate_stdlib $(Symbol("@code_llvm")) InteractiveUtils true + @eval @deprecate_stdlib $(Symbol("@code_native")) InteractiveUtils true end empty!(DEPOT_PATH) diff --git a/base/sysinfo.jl b/base/sysinfo.jl index 0c1f40420f4721..0104c18ab511de 100644 --- a/base/sysinfo.jl +++ b/base/sysinfo.jl @@ -109,14 +109,35 @@ CPUinfo(info::UV_cpu_info_t) = CPUinfo(unsafe_string(info.model), info.speed, info.cpu_times!user, info.cpu_times!nice, info.cpu_times!sys, info.cpu_times!idle, info.cpu_times!irq) -show(io::IO, info::CPUinfo) = Base._show_cpuinfo(io, info, true, " ") +function _show_cpuinfo(io::IO, info::Sys.CPUinfo, header::Bool=true, prefix::AbstractString=" ") + tck = SC_CLK_TCK + if header + println(io, info.model, ": ") + print(io, " "^length(prefix)) + println(io, " ", lpad("speed", 5), " ", lpad("user", 9), " ", lpad("nice", 9), " ", + lpad("sys", 9), " ", lpad("idle", 9), " ", lpad("irq", 9)) + end + print(io, prefix) + unit = tck > 0 ? " s " : " " + tc = max(tck, 1) + d(i, unit=unit) = lpad(string(round(Int,i)), 9) * unit + print(io, + lpad(string(info.speed), 5), " MHz ", + d(info.cpu_times!user / tc), d(info.cpu_times!nice / tc), d(info.cpu_times!sys / tc), + d(info.cpu_times!idle / tc), d(info.cpu_times!irq / tc, tck > 0 ? " s" : " ")) + if tck <= 0 + print(io, "ticks") + end +end + +show(io::IO, info::CPUinfo) = _show_cpuinfo(io, info, true, " ") function _cpu_summary(io::IO, cpu::AbstractVector{CPUinfo}, i, j) if j-i < 9 header = true for x = i:j header || println(io) - Base._show_cpuinfo(io, cpu[x], header, "#$(x-i+1) ") + _show_cpuinfo(io, cpu[x], header, "#$(x-i+1) ") header = false end else @@ -131,7 +152,7 @@ function _cpu_summary(io::IO, cpu::AbstractVector{CPUinfo}, i, j) summary.cpu_times!irq += cpu[x].cpu_times!irq end summary.speed = div(summary.speed,count) - Base._show_cpuinfo(io, summary, true, "#1-$(count) ") + _show_cpuinfo(io, summary, true, "#1-$(count) ") end println(io) end diff --git a/base/util.jl b/base/util.jl index 36eb119d5e816d..feddea5e464a6d 100644 --- a/base/util.jl +++ b/base/util.jl @@ -675,18 +675,36 @@ kwdef_val(::Type{T}) where {T<:Integer} = zero(T) kwdef_val(::Type{T}) where {T} = T() +# testing -function _check_bitarray_consistency(B::BitArray{N}) where N - n = length(B) - if N ≠ 1 - all(d ≥ 0 for d in B.dims) || (@warn("Negative d in dims: $(B.dims)"); return false) - prod(B.dims) ≠ n && (@warn("Inconsistent dims/len: prod(dims)=$(prod(B.dims)) len=$n"); return false) +""" + Base.runtests(tests=["all"]; ncores=ceil(Int, Sys.CPU_CORES / 2), + exit_on_error=false, [seed]) + +Run the Julia unit tests listed in `tests`, which can be either a string or an array of +strings, using `ncores` processors. If `exit_on_error` is `false`, when one test +fails, all remaining tests in other files will still be run; they are otherwise discarded, +when `exit_on_error == true`. +If a seed is provided via the keyword argument, it is used to seed the +global RNG in the context where the tests are run; otherwise the seed is chosen randomly. +""" +function runtests(tests = ["all"]; ncores = ceil(Int, Sys.CPU_CORES / 2), + exit_on_error=false, + seed::Union{BitInteger,Nothing}=nothing) + if isa(tests,AbstractString) + tests = split(tests) + end + exit_on_error && push!(tests, "--exit-on-error") + seed != nothing && push!(tests, "--seed=0x$(hex(seed % UInt128))") # cast to UInt128 to avoid a minus sign + ENV2 = copy(ENV) + ENV2["JULIA_CPU_CORES"] = "$ncores" + try + run(setenv(`$(julia_cmd()) $(joinpath(Sys.BINDIR, + Base.DATAROOTDIR, "julia", "test", "runtests.jl")) $tests`, ENV2)) + catch + buf = PipeBuffer() + versioninfo(buf) + error("A test has failed. Please submit a bug report (https://github.com/JuliaLang/julia/issues)\n" * + "including error messages above and the output of versioninfo():\n$(read(buf, String))") end - Bc = B.chunks - nc = length(Bc) - nc == num_bit_chunks(n) || (@warn("Incorrect chunks length for length $n: expected=$(num_bit_chunks(n)) actual=$nc"); return false) - n == 0 && return true - Bc[end] & _msk_end(n) == Bc[end] || (@warn("Nonzero bits in chunk after `BitArray` end"); return false) - return true end - diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 6bd021c94b1832..be3018e28ae17a 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -22,14 +22,7 @@ Base.exit Base.quit Base.atexit Base.isinteractive -Base.varinfo Base.summarysize -Base.edit(::AbstractString, ::Integer) -Base.edit(::Any) -Base.@edit -Base.less(::AbstractString) -Base.less(::Any) -Base.@less Base.clipboard(::Any) Base.clipboard() Base.require @@ -38,14 +31,10 @@ Base.__precompile__ Base.include Base.include_string Base.include_dependency -Base.Docs.apropos Base.which(::Any, ::Any) Base.which(::Symbol) -Base.@which Base.methods -Base.methodswith Base.@show -Base.versioninfo ans ``` @@ -136,7 +125,6 @@ Base.identity Base.supertype Core.:(<:) Base.:(>:) -Base.subtypes Base.typejoin Base.typeintersect Base.promote_type @@ -341,7 +329,6 @@ Base.isconst Base.nameof(::Function) Base.functionloc(::Any, ::Any) Base.functionloc(::Method) -Base.@functionloc ``` ## Internals @@ -357,14 +344,6 @@ Base.macroexpand Base.@macroexpand Base.@macroexpand1 Base.code_lowered -Base.@code_lowered Base.code_typed -Base.@code_typed -Base.code_warntype -Base.@code_warntype -Base.code_llvm -Base.@code_llvm -Base.code_native -Base.@code_native Base.precompile ``` diff --git a/stdlib/Dates/test/ranges.jl b/stdlib/Dates/test/ranges.jl index 33099a2fd5de38..3edf61dff123ad 100644 --- a/stdlib/Dates/test/ranges.jl +++ b/stdlib/Dates/test/ranges.jl @@ -5,6 +5,8 @@ module RangesTest using Test using Dates +using InteractiveUtils: subtypes + let for T in (Dates.Date, Dates.DateTime) f1 = T(2014); l1 = T(2013, 12, 31) diff --git a/stdlib/InteractiveUtils/docs/src/index.md b/stdlib/InteractiveUtils/docs/src/index.md new file mode 100644 index 00000000000000..39555d32201eeb --- /dev/null +++ b/stdlib/InteractiveUtils/docs/src/index.md @@ -0,0 +1,25 @@ +# Interactive Utilities + +```@docs +InteractiveUtils.apropos +InteractiveUtils.varinfo +InteractiveUtils.versioninfo +InteractiveUtils.methodswith +InteractiveUtils.subtypes +InteractiveUtils.edit(::AbstractString, ::Integer) +InteractiveUtils.edit(::Any) +InteractiveUtils.@edit +InteractiveUtils.less(::AbstractString) +InteractiveUtils.less(::Any) +InteractiveUtils.@less +InteractiveUtils.@which +InteractiveUtils.@functionloc +InteractiveUtils.@code_lowered +InteractiveUtils.@code_typed +InteractiveUtils.code_warntype +InteractiveUtils.@code_warntype +InteractiveUtils.code_llvm +InteractiveUtils.@code_llvm +InteractiveUtils.code_native +InteractiveUtils.@code_native +``` diff --git a/stdlib/InteractiveUtils/src/InteractiveUtils.jl b/stdlib/InteractiveUtils/src/InteractiveUtils.jl new file mode 100644 index 00000000000000..6e116b2d2047e9 --- /dev/null +++ b/stdlib/InteractiveUtils/src/InteractiveUtils.jl @@ -0,0 +1,340 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +__precompile__(true) + +module InteractiveUtils + +export apropos, edit, less, code_warntype, code_llvm, code_native, methodswith, varinfo, + versioninfo, subtypes, @which, @edit, @less, @functionloc, @code_warntype, @code_typed, + @code_lowered, @code_llvm, @code_native + +import Base.Docs.apropos + +using Base: unwrap_unionall, rewrap_unionall, isdeprecated, Bottom, show_expr_type, show_unquoted, summarysize, + to_tuple_type, signature_type, format_bytes + +using Markdown + +include("editless.jl") +include("codeview.jl") +include("macros.jl") + +""" + varinfo(m::Module=Main, pattern::Regex=r"") + +Return a markdown table giving information about exported global variables in a module, optionally restricted +to those matching `pattern`. + +The memory consumption estimate is an approximate lower bound on the size of the internal structure of the object. +""" +function varinfo(m::Module=Main, pattern::Regex=r"") + rows = + Any[ let value = getfield(m, v) + Any[string(v), + (value===Base || value===Main || value===Core ? "" : format_bytes(summarysize(value))), + summary(value)] + end + for v in sort!(names(m)) if isdefined(m, v) && contains(string(v), pattern) ] + + pushfirst!(rows, Any["name", "size", "summary"]) + + return Markdown.MD(Any[Markdown.Table(rows, Symbol[:l, :r, :l])]) +end +varinfo(pat::Regex) = varinfo(Main, pat) + +""" + versioninfo(io::IO=STDOUT; verbose::Bool=false, packages::Bool=false) + +Print information about the version of Julia in use. The output is +controlled with boolean keyword arguments: + +- `packages`: print information about installed packages +- `verbose`: print all additional information +""" +function versioninfo(io::IO=STDOUT; verbose::Bool=false, packages::Bool=false) + println(io, "Julia Version $VERSION") + if !isempty(Base.GIT_VERSION_INFO.commit_short) + println(io, "Commit $(Base.GIT_VERSION_INFO.commit_short) ($(Base.GIT_VERSION_INFO.date_string))") + end + if ccall(:jl_is_debugbuild, Cint, ())!=0 + println(io, "DEBUG build") + end + println(io, "Platform Info:") + println(io, " OS: ", Sys.iswindows() ? "Windows" : Sys.isapple() ? + "macOS" : Sys.KERNEL, " (", Sys.MACHINE, ")") + + if verbose + lsb = "" + if Sys.islinux() + try lsb = readchomp(pipeline(`lsb_release -ds`, stderr=DevNull)) end + end + if Sys.iswindows() + try lsb = strip(read(`$(ENV["COMSPEC"]) /c ver`, String)) end + end + if !isempty(lsb) + println(io, " ", lsb) + end + if Sys.isunix() + println(io, " uname: ", readchomp(`uname -mprsv`)) + end + end + + if verbose + cpuio = IOBuffer() # print cpu_summary with correct alignment + Sys.cpu_summary(cpuio) + for (i, line) in enumerate(split(String(take!(cpuio)), "\n")) + prefix = i == 1 ? " CPU: " : " " + println(io, prefix, line) + end + else + cpu = Sys.cpu_info() + println(io, " CPU: ", cpu[1].model) + end + + if verbose + println(io, " Memory: $(Sys.total_memory()/2^30) GB ($(Sys.free_memory()/2^20) MB free)") + try println(io, " Uptime: $(Sys.uptime()) sec") end + print(io, " Load Avg: ") + Base.print_matrix(io, Sys.loadavg()') + println(io) + end + println(io, " WORD_SIZE: ", Sys.WORD_SIZE) + println(io, " LIBM: ",Base.libm_name) + println(io, " LLVM: libLLVM-",Base.libllvm_version," (", Sys.JIT, ", ", Sys.CPU_NAME, ")") + + println(io, "Environment:") + for (k,v) in ENV + if contains(String(k), r"JULIA") + println(io, " $(k) = $(v)") + end + end + if verbose + for (k,v) in ENV + if contains(String(k), r"PATH|FLAG|^TERM$|HOME") + println(io, " $(k) = $(v)") + end + end + end + if packages || verbose + println(io, "Packages:") + println(io, " Package Directory: ", Pkg.dir()) + print(io, " Package Status:") + if isdir(Pkg.dir()) + println(io, "") + Pkg.status(io) + else + println(io, " no packages installed") + end + end +end + + +function type_close_enough(@nospecialize(x), @nospecialize(t)) + x == t && return true + # TODO: handle UnionAll properly + return (isa(x, DataType) && isa(t, DataType) && x.name === t.name && x <: t) || + (isa(x, Union) && isa(t, DataType) && (type_close_enough(x.a, t) || type_close_enough(x.b, t))) +end + +# `methodswith` -- shows a list of methods using the type given +""" + methodswith(typ[, module or function]; supertypes::Bool=false]) + +Return an array of methods with an argument of type `typ`. + +The optional second argument restricts the search to a particular module or function +(the default is all top-level modules). + +If keyword `supertypes` is `true`, also return arguments with a parent type of `typ`, +excluding type `Any`. +""" +function methodswith(t::Type, f::Function, meths = Method[]; supertypes::Bool=false) + for d in methods(f) + if any(function (x) + let x = rewrap_unionall(x, d.sig) + (type_close_enough(x, t) || + (supertypes ? (t <: x && (!isa(x,TypeVar) || x.ub != Any)) : + (isa(x,TypeVar) && x.ub != Any && t == x.ub)) && + x != Any) + end + end, + unwrap_unionall(d.sig).parameters) + push!(meths, d) + end + end + return meths +end + +function _methodswith(t::Type, m::Module, supertypes::Bool) + meths = Method[] + for nm in names(m) + if isdefined(m, nm) + f = getfield(m, nm) + if isa(f, Function) + methodswith(t, f, meths; supertypes = supertypes) + end + end + end + return unique(meths) +end + +methodswith(t::Type, m::Module; supertypes::Bool=false) = _methodswith(t, m, supertypes) + +function methodswith(t::Type; supertypes::Bool=false) + meths = Method[] + for mod in Base.loaded_modules_array() + append!(meths, _methodswith(t, mod, supertypes)) + end + return unique(meths) +end + +# subtypes +function _subtypes(m::Module, x::Type, sts=Set{Any}(), visited=Set{Module}()) + push!(visited, m) + xt = unwrap_unionall(x) + if !isa(xt, DataType) + return sts + end + xt = xt::DataType + for s in names(m, all = true) + if isdefined(m, s) && !isdeprecated(m, s) + t = getfield(m, s) + if isa(t, DataType) + t = t::DataType + if t.name.name === s && supertype(t).name == xt.name + ti = typeintersect(t, x) + ti != Bottom && push!(sts, ti) + end + elseif isa(t, UnionAll) + t = t::UnionAll + tt = unwrap_unionall(t) + isa(tt, DataType) || continue + tt = tt::DataType + if tt.name.name === s && supertype(tt).name == xt.name + ti = typeintersect(t, x) + ti != Bottom && push!(sts, ti) + end + elseif isa(t, Module) + t = t::Module + in(t, visited) || _subtypes(t, x, sts, visited) + end + end + end + return sts +end + +function _subtypes_in(mods::Array, x::Type) + if !isabstracttype(x) + # Fast path + return Type[] + end + sts = Set{Any}() + visited = Set{Module}() + for m in mods + _subtypes(m, x, sts, visited) + end + return sort!(collect(sts), by=string) +end + +subtypes(m::Module, x::Type) = _subtypes_in([m], x) + +""" + subtypes(T::DataType) + +Return a list of immediate subtypes of DataType `T`. Note that all currently loaded subtypes +are included, including those not visible in the current module. + +# Examples +```jldoctest +julia> subtypes(Integer) +3-element Array{Union{DataType, UnionAll},1}: + Bool + Signed + Unsigned +``` +""" +subtypes(x::Type) = _subtypes_in(Base.loaded_modules_array(), x) + +# dumptype is for displaying abstract type hierarchies, +# based on Jameson Nash's examples/typetree.jl +function dumptype(io::IO, @nospecialize(x), n::Int, indent) + print(io, x) + n == 0 && return # too deeply nested + isa(x, DataType) && x.abstract && dumpsubtypes(io, x, Main, n, indent) + nothing +end + +directsubtype(a::DataType, b::DataType) = supertype(a).name === b.name +directsubtype(a::UnionAll, b::DataType) = directsubtype(a.body, b) +directsubtype(a::Union, b::DataType) = directsubtype(a.a, b) || directsubtype(a.b, b) +# Fallback to handle TypeVar's +directsubtype(a, b::DataType) = false +function dumpsubtypes(io::IO, x::DataType, m::Module, n::Int, indent) + for s in names(m, all = true) + if isdefined(m, s) && !isdeprecated(m, s) + t = getfield(m, s) + if t === x || t === m + continue + elseif isa(t, Module) && nameof(t) === s && parentmodule(t) === m + # recurse into primary module bindings + dumpsubtypes(io, x, t, n, indent) + elseif isa(t, UnionAll) && directsubtype(t::UnionAll, x) + dt = unwrap_unionall(t) + println(io) + if isa(dt, DataType) && dt.name.wrapper === t + # primary type binding + print(io, indent, " ") + dumptype(io, dt, n - 1, string(indent, " ")) + else + # aliases to types + print(io, indent, " ", m, ".", s, "{") + tvar_io::IOContext = io + tp = t + while true + show(tvar_io, tp.var) + tvar_io = IOContext(tvar_io, :unionall_env => tp.var) + tp = tp.body + if isa(tp, UnionAll) + print(io, ", ") + else + print(io, "} = ") + break + end + end + show(tvar_io, tp) + end + elseif isa(t, Union) && directsubtype(t::Union, x) + println(io) + print(io, indent, " ", m, ".", s, " = ", t) + elseif isa(t, DataType) && directsubtype(t::DataType, x) + println(io) + if t.name.module !== m || t.name.name != s + # aliases to types + print(io, indent, " ", m, ".", s, " = ") + show(io, t) + else + # primary type binding + print(io, indent, " ") + dumptype(io, t, n - 1, string(indent, " ")) + end + end + end + end + nothing +end + +@deprecate methodswith(typ, supertypes) methodswith(typ, supertypes = supertypes) +@deprecate whos(io::IO, m::Module, pat::Regex) show(io, varinfo(m, pat)) +@deprecate whos(io::IO, m::Module) show(io, varinfo(m)) +@deprecate whos(io::IO) show(io, varinfo()) +@deprecate whos(m::Module, pat::Regex) varinfo(m, pat) +@deprecate whos(m::Module) varinfo(m) +@deprecate whos(pat::Regex) varinfo(pat) +@deprecate whos() varinfo() +@deprecate code_native(io, f, types, syntax) code_native(io, f, types, syntax = syntax) +@deprecate code_native(f, types, syntax) code_native(f, types, syntax = syntax) +# PR #21974 +@deprecate versioninfo(verbose::Bool) versioninfo(verbose=verbose) +@deprecate versioninfo(io::IO, verbose::Bool) versioninfo(io, verbose=verbose) + +end diff --git a/stdlib/InteractiveUtils/src/codeview.jl b/stdlib/InteractiveUtils/src/codeview.jl new file mode 100644 index 00000000000000..c9516e8666761b --- /dev/null +++ b/stdlib/InteractiveUtils/src/codeview.jl @@ -0,0 +1,136 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# displaying type warnings + +""" + code_warntype([io::IO], f, types) + +Prints lowered and type-inferred ASTs for the methods matching the given generic function +and type signature to `io` which defaults to `STDOUT`. The ASTs are annotated in such a way +as to cause "non-leaf" types to be emphasized (if color is available, displayed in red). +This serves as a warning of potential type instability. Not all non-leaf types are particularly +problematic for performance, so the results need to be used judiciously. +In particular, unions containing either [`missing`](@ref) or [`nothing`](@ref) are displayed in yellow, since +these are often intentional. +See [`@code_warntype`](@ref man-code-warntype) for more information. +""" +function code_warntype(io::IO, f, @nospecialize(t)) + function slots_used(ci, slotnames) + used = falses(length(slotnames)) + scan_exprs!(used, ci.code) + return used + end + + function scan_exprs!(used, exprs) + for ex in exprs + if isa(ex, Slot) + used[ex.id] = true + elseif isa(ex, Expr) + scan_exprs!(used, ex.args) + end + end + end + + emph_io = IOContext(io, :TYPEEMPHASIZE => true) + for (src, rettype) in code_typed(f, t) + println(emph_io, "Variables:") + slotnames = Base.sourceinfo_slotnames(src) + used_slotids = slots_used(src, slotnames) + for i = 1:length(slotnames) + if used_slotids[i] + print(emph_io, " ", slotnames[i]) + if isa(src.slottypes, Array) + show_expr_type(emph_io, src.slottypes[i], true) + end + print(emph_io, '\n') + elseif !('#' in slotnames[i] || '@' in slotnames[i]) + print(emph_io, " ", slotnames[i], "\n") + end + end + print(emph_io, "\nBody:\n ") + body = Expr(:body) + body.args = src.code + body.typ = rettype + # Fix slot names and types in function body + show_unquoted(IOContext(emph_io, :SOURCEINFO => src, :SOURCE_SLOTNAMES => slotnames), + body, 2) + print(emph_io, '\n') + end + nothing +end +code_warntype(f, @nospecialize(t)) = code_warntype(STDOUT, f, t) + +import Base.CodegenParams + +# Printing code representations in IR and assembly +function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrapper::Bool, + strip_ir_metadata::Bool, dump_module::Bool, syntax::Symbol=:att, + optimize::Bool=true, params::CodegenParams=CodegenParams()) + ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") + if isa(f, Core.Builtin) + throw(ArgumentError("argument is not a generic function")) + end + # get the MethodInstance for the method match + world = typemax(UInt) + meth = which(f, t) + t = to_tuple_type(t) + tt = signature_type(f, t) + (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), tt, meth.sig)::SimpleVector + meth = Base.func_for_method_checked(meth, ti) + linfo = ccall(:jl_specializations_get_linfo, Ref{Core.MethodInstance}, (Any, Any, Any, UInt), meth, ti, env, world) + # get the code for it + return _dump_function_linfo(linfo, world, native, wrapper, strip_ir_metadata, dump_module, syntax, optimize, params) +end + +function _dump_function_linfo(linfo::Core.MethodInstance, world::UInt, native::Bool, wrapper::Bool, + strip_ir_metadata::Bool, dump_module::Bool, syntax::Symbol=:att, + optimize::Bool=true, params::CodegenParams=CodegenParams()) + if syntax != :att && syntax != :intel + throw(ArgumentError("'syntax' must be either :intel or :att")) + end + if native + llvmf = ccall(:jl_get_llvmf_decl, Ptr{Cvoid}, (Any, UInt, Bool, CodegenParams), linfo, world, wrapper, params) + else + llvmf = ccall(:jl_get_llvmf_defn, Ptr{Cvoid}, (Any, UInt, Bool, Bool, CodegenParams), linfo, world, wrapper, optimize, params) + end + if llvmf == C_NULL + error("could not compile the specified method") + end + + if native + str = ccall(:jl_dump_function_asm, Ref{String}, + (Ptr{Cvoid}, Cint, Ptr{UInt8}), llvmf, 0, syntax) + else + str = ccall(:jl_dump_function_ir, Ref{String}, + (Ptr{Cvoid}, Bool, Bool), llvmf, strip_ir_metadata, dump_module) + end + + # TODO: use jl_is_cacheable_sig instead of isdispatchtuple + isdispatchtuple(linfo.specTypes) || (str = "; WARNING: This code may not match what actually runs.\n" * str) + return str +end + +""" + code_llvm([io=STDOUT,], f, types) + +Prints the LLVM bitcodes generated for running the method matching the given generic +function and type signature to `io`. + +All metadata and dbg.* calls are removed from the printed bitcode. Use code_llvm_raw for the full IR. +""" +code_llvm(io::IO, @nospecialize(f), @nospecialize(types=Tuple), strip_ir_metadata=true, dump_module=false) = + print(io, _dump_function(f, types, false, false, strip_ir_metadata, dump_module)) +code_llvm(@nospecialize(f), @nospecialize(types=Tuple)) = code_llvm(STDOUT, f, types) +code_llvm_raw(@nospecialize(f), @nospecialize(types=Tuple)) = code_llvm(STDOUT, f, types, false) + +""" + code_native([io=STDOUT,], f, types; syntax = :att) + +Prints the native assembly instructions generated for running the method matching the given +generic function and type signature to `io`. +Switch assembly syntax using `syntax` symbol parameter set to `:att` for AT&T syntax or `:intel` for Intel syntax. +""" +code_native(io::IO, @nospecialize(f), @nospecialize(types=Tuple); syntax::Symbol = :att) = + print(io, _dump_function(f, types, true, false, false, false, syntax)) +code_native(@nospecialize(f), @nospecialize(types=Tuple); syntax::Symbol = :att) = code_native(STDOUT, f, types, syntax = syntax) +code_native(::IO, ::Any, ::Symbol) = error("illegal code_native call") # resolve ambiguous call diff --git a/stdlib/InteractiveUtils/src/editless.jl b/stdlib/InteractiveUtils/src/editless.jl new file mode 100644 index 00000000000000..5aa187f5eac6e9 --- /dev/null +++ b/stdlib/InteractiveUtils/src/editless.jl @@ -0,0 +1,126 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# editing and paging files + +import Base.shell_split + +""" + editor() + +Determine the editor to use when running functions like `edit`. Return an `Array` compatible +for use within backticks. You can change the editor by setting `JULIA_EDITOR`, `VISUAL` or +`EDITOR` as an environment variable. +""" +function editor() + if Sys.iswindows() || Sys.isapple() + default_editor = "open" + elseif isfile("/etc/alternatives/editor") + default_editor = "/etc/alternatives/editor" + else + default_editor = "emacs" + end + # Note: the editor path can include spaces (if escaped) and flags. + args = shell_split(get(ENV,"JULIA_EDITOR", get(ENV,"VISUAL", get(ENV,"EDITOR", default_editor)))) + isempty(args) && error("editor is empty") + return args +end + +""" + edit(path::AbstractString, line::Integer=0) + +Edit a file or directory optionally providing a line number to edit the file at. +Return to the `julia` prompt when you quit the editor. The editor can be changed +by setting `JULIA_EDITOR`, `VISUAL` or `EDITOR` as an environment variable. +""" +function edit(path::AbstractString, line::Integer=0) + command = editor() + name = basename(first(command)) + if endswith(path, ".jl") + f = find_source_file(path) + f !== nothing && (path = f) + end + background = true + line_unsupported = false + if startswith(name, "vim.") || name == "vi" || name == "vim" || name == "nvim" || + name == "mvim" || name == "nano" || + name == "emacs" && any(c -> c in ["-nw", "--no-window-system" ], command) || + name == "emacsclient" && any(c -> c in ["-nw", "-t", "-tty"], command) + cmd = line != 0 ? `$command +$line $path` : `$command $path` + background = false + elseif startswith(name, "emacs") || name == "gedit" || startswith(name, "gvim") + cmd = line != 0 ? `$command +$line $path` : `$command $path` + elseif name == "textmate" || name == "mate" || name == "kate" + cmd = line != 0 ? `$command $path -l $line` : `$command $path` + elseif startswith(name, "subl") || startswith(name, "atom") + cmd = line != 0 ? `$command $path:$line` : `$command $path` + elseif name == "code" || (Sys.iswindows() && uppercase(name) == "CODE.EXE") + cmd = line != 0 ? `$command -g $path:$line` : `$command -g $path` + elseif startswith(name, "notepad++") + cmd = line != 0 ? `$command $path -n$line` : `$command $path` + elseif Sys.isapple() && name == "open" + cmd = `open -t $path` + line_unsupported = true + else + cmd = `$command $path` + background = false + line_unsupported = true + end + + if Sys.iswindows() && name == "open" + @static Sys.iswindows() && # don't emit this ccall on other platforms + systemerror(:edit, ccall((:ShellExecuteW, "shell32"), stdcall, Int, + (Ptr{Cvoid}, Cwstring, Cwstring, Ptr{Cvoid}, Ptr{Cvoid}, Cint), + C_NULL, "open", path, C_NULL, C_NULL, 10) ≤ 32) + elseif background + spawn(pipeline(cmd, stderr=STDERR)) + else + run(cmd) + end + line != 0 && line_unsupported && println("Unknown editor: no line number information passed.\nThe method is defined at line $line.") + + nothing +end + +""" + edit(function, [types]) + +Edit the definition of a function, optionally specifying a tuple of types to +indicate which method to edit. The editor can be changed by setting `JULIA_EDITOR`, +`VISUAL` or `EDITOR` as an environment variable. +""" +edit(f) = edit(functionloc(f)...) +edit(f, @nospecialize t) = edit(functionloc(f,t)...) +edit(file, line::Integer) = error("could not find source file for function") + +# terminal pager + +if Sys.iswindows() + function less(file::AbstractString, line::Integer) + pager = shell_split(get(ENV, "PAGER", "more")) + g = pager[1] == "more" ? "" : "g" + run(Cmd(`$pager +$(line)$(g) \"$file\"`, windows_verbatim = true)) + end +else + function less(file::AbstractString, line::Integer) + pager = shell_split(get(ENV, "PAGER", "less")) + run(`$pager +$(line)g $file`) + end +end + +""" + less(file::AbstractString, [line::Integer]) + +Show a file using the default pager, optionally providing a starting line number. Returns to +the `julia` prompt when you quit the pager. +""" +less(file::AbstractString) = less(file, 1) + +""" + less(function, [types]) + +Show the definition of a function using the default pager, optionally specifying a tuple of +types to indicate which method to see. +""" +less(f) = less(functionloc(f)...) +less(f, @nospecialize t) = less(functionloc(f,t)...) +less(file, line::Integer) = error("could not find source file for function") diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl new file mode 100644 index 00000000000000..11f8d55f5e11db --- /dev/null +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -0,0 +1,164 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# macro wrappers for various reflection functions + +import Base.typesof + +function gen_call_with_extracted_types(__module__, fcn, ex0) + if isa(ex0, Expr) + if any(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args) + # remove keyword args, but call the kwfunc + args = filter(a->!(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args) + return quote + local arg1 = $(esc(args[1])) + $(fcn)(Core.kwfunc(arg1), + Tuple{Any, Core.Typeof(arg1), + $(typesof)($(map(esc, args[2:end])...)).parameters...}) + end + elseif ex0.head == :call + return Expr(:call, fcn, esc(ex0.args[1]), + Expr(:call, typesof, map(esc, ex0.args[2:end])...)) + elseif ex0.head == :(.) + return Expr(:call, fcn, Base.getproperty, + Expr(:call, typesof, map(esc, ex0.args)...)) + elseif ex0.head == :(=) && length(ex0.args) == 2 && ex0.args[1].head == :(.) + return Expr(:call, fcn, Base.setproperty!, + Expr(:call, typesof, map(esc, [ex0.args[1].args..., ex0.args[2]])...)) + end + end + if isa(ex0, Expr) && ex0.head == :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions* + return Expr(:call, fcn, esc(ex0.args[1]), Tuple{#=__source__=#LineNumberNode, #=__module__=#Module, Any[ Core.Typeof(a) for a in ex0.args[3:end] ]...}) + end + ex = Meta.lower(__module__, ex0) + exret = Expr(:none) + if !isa(ex, Expr) + exret = Expr(:call, :error, "expression is not a function call or symbol") + elseif ex.head == :call + if any(e->(isa(e, Expr) && e.head==:(...)), ex0.args) && + (ex.args[1] === GlobalRef(Core,:_apply) || + ex.args[1] === GlobalRef(Base,:_apply)) + # check for splatting + exret = Expr(:call, ex.args[1], fcn, + Expr(:tuple, esc(ex.args[2]), + Expr(:call, typesof, map(esc, ex.args[3:end])...))) + else + exret = Expr(:call, fcn, esc(ex.args[1]), + Expr(:call, typesof, map(esc, ex.args[2:end])...)) + end + elseif ex.head == :body + a1 = ex.args[1] + if isa(a1, Expr) && a1.head == :call + a11 = a1.args[1] + if a11 == :setindex! + exret = Expr(:call, fcn, a11, + Expr(:call, typesof, map(esc, a1.args[2:end])...)) + end + end + end + if ex.head == :thunk || exret.head == :none + exret = Expr(:call, :error, "expression is not a function call, " + * "or is too complex for @$fcn to analyze; " + * "break it down to simpler parts if possible") + end + return exret +end + +for fname in [:which, :less, :edit, :functionloc, :code_warntype, + :code_llvm, :code_llvm_raw, :code_native] + @eval begin + macro ($fname)(ex0) + gen_call_with_extracted_types(__module__, $(Expr(:quote, fname)), ex0) + end + end +end +macro which(ex0::Symbol) + ex0 = QuoteNode(ex0) + return :(which($__module__, $ex0)) +end + +for fname in [:code_typed, :code_lowered] + @eval begin + macro ($fname)(ex0) + thecall = gen_call_with_extracted_types(__module__, $(Expr(:quote, fname)), ex0) + quote + results = $thecall + length(results) == 1 ? results[1] : results + end + end + end +end + +""" + @functionloc + +Applied to a function or macro call, it evaluates the arguments to the specified call, and +returns a tuple `(filename,line)` giving the location for the method that would be called for those arguments. +It calls out to the `functionloc` function. +""" +:@functionloc + +""" + @which + +Applied to a function or macro call, it evaluates the arguments to the specified call, and +returns the `Method` object for the method that would be called for those arguments. Applied +to a variable, it returns the module in which the variable was bound. It calls out to the +`which` function. +""" +:@which + +""" + @less + +Evaluates the arguments to the function or macro call, determines their types, and calls the `less` +function on the resulting expression. +""" +:@less + +""" + @edit + +Evaluates the arguments to the function or macro call, determines their types, and calls the `edit` +function on the resulting expression. +""" +:@edit + +""" + @code_typed + +Evaluates the arguments to the function or macro call, determines their types, and calls +[`code_typed`](@ref) on the resulting expression. +""" +:@code_typed + +""" + @code_lowered + +Evaluates the arguments to the function or macro call, determines their types, and calls +[`code_lowered`](@ref) on the resulting expression. +""" +:@code_lowered + +""" + @code_warntype + +Evaluates the arguments to the function or macro call, determines their types, and calls +[`code_warntype`](@ref) on the resulting expression. +""" +:@code_warntype + +""" + @code_llvm + +Evaluates the arguments to the function or macro call, determines their types, and calls +[`code_llvm`](@ref) on the resulting expression. +""" +:@code_llvm + +""" + @code_native + +Evaluates the arguments to the function or macro call, determines their types, and calls +[`code_native`](@ref) on the resulting expression. +""" +:@code_native diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl new file mode 100644 index 00000000000000..9747a0d035f08b --- /dev/null +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -0,0 +1,295 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using InteractiveUtils + +# test methodswith +# `methodswith` relies on exported symbols +export func4union, Base +struct NoMethodHasThisType end +@test isempty(methodswith(NoMethodHasThisType)) +@test !isempty(methodswith(Int)) +struct Type4Union end +func4union(::Union{Type4Union,Int}) = () +@test !isempty(methodswith(Type4Union, @__MODULE__)) + +# PR #19964 +@test isempty(subtypes(Float64)) + +# Issue #20086 +abstract type A20086{T,N} end +struct B20086{T,N} <: A20086{T,N} end +@test subtypes(A20086) == [B20086] +@test subtypes(A20086{Int}) == [B20086{Int}] +@test subtypes(A20086{T,3} where T) == [B20086{T,3} where T] +@test subtypes(A20086{Int,3}) == [B20086{Int,3}] + +# code_warntype +module WarnType +using Test, Random, InteractiveUtils + +function warntype_hastag(f, types, tag) + iob = IOBuffer() + code_warntype(iob, f, types) + str = String(take!(iob)) + return contains(str, tag) +end + +pos_stable(x) = x > 0 ? x : zero(x) +pos_unstable(x) = x > 0 ? x : 0 + +tag = "UNION" +@test warntype_hastag(pos_unstable, Tuple{Float64}, tag) +@test !warntype_hastag(pos_stable, Tuple{Float64}, tag) + +mutable struct Stable{T,N} + A::Array{T,N} +end +mutable struct Unstable{T} + A::Array{T} +end +Base.getindex(A::Stable, i) = A.A[i] +Base.getindex(A::Unstable, i) = A.A[i] + +tag = "ARRAY{FLOAT64,N}" +@test warntype_hastag(getindex, Tuple{Unstable{Float64},Int}, tag) +@test !warntype_hastag(getindex, Tuple{Stable{Float64,2},Int}, tag) +@test warntype_hastag(getindex, Tuple{Stable{Float64},Int}, tag) + +# Make sure emphasis is not used for other functions +tag = "ANY" +iob = IOBuffer() +show(iob, Meta.lower(Main, :(x -> x^2))) +str = String(take!(iob)) +@test !contains(str, tag) + +# Make sure non used variables are not emphasized +has_unused() = (a = rand(5)) +@test !warntype_hastag(has_unused, Tuple{}, tag) +@test warntype_hastag(has_unused, Tuple{}, "") + +# Make sure that "expected" unions are highlighted with warning color instead of error color +iob = IOBuffer() +code_warntype(IOContext(iob, :color => true), x -> (x > 1 ? "foo" : nothing), Tuple{Int64}) +str = String(take!(iob)) +@test contains(str, Base.text_colors[Base.warn_color()]) + +# Make sure getproperty and setproperty! works with @code_... macros +struct T1234321 + t::Int +end +Base.getproperty(t::T1234321, ::Symbol) = "foo" +@test (@code_typed T1234321(1).f).second == String +Base.setproperty!(t::T1234321, ::Symbol, ::Symbol) = "foo" +@test (@code_typed T1234321(1).f = :foo).second == String + +module ImportIntrinsics15819 +# Make sure changing the lookup path of an intrinsic doesn't break +# the heuristic for type instability warning. +import Core.Intrinsics: sqrt_llvm, bitcast +# Use import +sqrt15819(x::Float64) = bitcast(Float64, sqrt_llvm(x)) +# Use fully qualified name +sqrt15819(x::Float32) = bitcast(Float32, Core.Intrinsics.sqrt_llvm(x)) +end # module ImportIntrinsics15819 + +foo11122(x) = @fastmath x - 1.0 + +# issue #11122, #13568 and #15819 +@test !warntype_hastag(+, Tuple{Int,Int}, tag) +@test !warntype_hastag(-, Tuple{Int,Int}, tag) +@test !warntype_hastag(*, Tuple{Int,Int}, tag) +@test !warntype_hastag(/, Tuple{Int,Int}, tag) +@test !warntype_hastag(foo11122, Tuple{Float32}, tag) +@test !warntype_hastag(foo11122, Tuple{Float64}, tag) +@test !warntype_hastag(foo11122, Tuple{Int}, tag) +@test !warntype_hastag(sqrt, Tuple{Int}, tag) +@test !warntype_hastag(sqrt, Tuple{Float64}, tag) +@test !warntype_hastag(^, Tuple{Float64,Int32}, tag) +@test !warntype_hastag(^, Tuple{Float32,Int32}, tag) +@test !warntype_hastag(ImportIntrinsics15819.sqrt15819, Tuple{Float64}, tag) +@test !warntype_hastag(ImportIntrinsics15819.sqrt15819, Tuple{Float32}, tag) + +end # module WarnType + +# Adds test for PR #17636 +let a = @code_typed 1 + 1 + b = @code_lowered 1 + 1 + @test isa(a, Pair{CodeInfo, DataType}) + @test isa(b, CodeInfo) + @test isa(a[1].code, Array{Any,1}) + @test isa(b.code, Array{Any,1}) + + function thing(a::Array, b::Real) + println("thing") + end + function thing(a::AbstractArray, b::Int) + println("blah") + end + @test_throws MethodError thing(rand(10), 1) + a = @code_typed thing(rand(10), 1) + b = @code_lowered thing(rand(10), 1) + @test length(a) == 0 + @test length(b) == 0 +end + +# Issue #16326 +mktemp() do f, io + OLDSTDOUT = STDOUT + redirect_stdout(io) + @test try @code_native map(abs, rand(3)); true; catch; false; end + redirect_stdout(OLDSTDOUT) + nothing +end + +module _test_varinfo_ +export x +x = 1.0 +end +@test repr(varinfo(Main, r"^$")) == """ +| name | size | summary | +|:---- | ----:|:------- | +""" +let v = repr(varinfo(_test_varinfo_)) + @test contains(v, "| x | 8 bytes | Float64 |") +end + +# Issue 14173 +module Tmp14173 + using Random + export A + A = randn(2000, 2000) +end +varinfo(Tmp14173) # warm up +const MEMDEBUG = ccall(:jl_is_memdebug, Bool, ()) +@test @allocated(varinfo(Tmp14173)) < (MEMDEBUG ? 60000 : 20000) + +# PR #24997: test that `varinfo` doesn't fail when encountering `missing` +module A + using InteractiveUtils + export missing + varinfo(A) +end + +# PR #23075 +@testset "versioninfo" begin + # check that versioninfo(io; verbose=true) doesn't error, produces some output + # and doesn't invoke Pkg.status which will error if JULIA_PKGDIR is set + mktempdir() do dir + withenv("JULIA_PKGDIR" => dir) do + buf = PipeBuffer() + versioninfo(buf, verbose=true) + ver = read(buf, String) + @test startswith(ver, "Julia Version $VERSION") + @test contains(ver, "Environment:") + @test contains(ver, "Package Status:") + @test contains(ver, "no packages installed") + @test isempty(readdir(dir)) + end + end +end + +const curmod = @__MODULE__ +const curmod_name = fullname(curmod) +const curmod_str = curmod === Main ? "Main" : join(curmod_name, ".") + +@test_throws ErrorException("\"this_is_not_defined\" is not defined in module $curmod_str") @which this_is_not_defined +# issue #13264 +@test isa((@which vcat(1...)), Method) + +# issue #13464 +let t13464 = "hey there sailor" + try + @which t13464[1,1] = (1.0,true) + error("unexpected") + catch err13464 + @test startswith(err13464.msg, "expression is not a function call, or is too complex") + end +end + +module MacroTest +export @macrotest +macro macrotest(x::Int, y::Symbol) end +macro macrotest(x::Int, y::Int) + nothing #This is here because of #15280 +end +end + +let + using .MacroTest + a = 1 + m = getfield(@__MODULE__, Symbol("@macrotest")) + @test which(m, Tuple{LineNumberNode, Module, Int, Symbol}) == @which @macrotest 1 a + @test which(m, Tuple{LineNumberNode, Module, Int, Int}) == @which @macrotest 1 1 + + @test first(methods(m, Tuple{LineNumberNode, Module, Int, Int})) == @which MacroTest.@macrotest 1 1 + @test functionloc(@which @macrotest 1 1) == @functionloc @macrotest 1 1 +end + +mutable struct A18434 +end +A18434(x; y=1) = 1 + +global counter18434 = 0 +function get_A18434() + global counter18434 + counter18434 += 1 + return A18434 +end +@which get_A18434()(1; y=2) +@test counter18434 == 1 +@which get_A18434()(1, y=2) +@test counter18434 == 2 + +let _true = Ref(true), f, g, h + @noinline f() = ccall((:time, "error_library_doesnt_exist\0"), Cvoid, ()) # some expression that throws an error in codegen + @noinline g() = _true[] ? 0 : h() + @noinline h() = (g(); f()) + @test_throws ErrorException @code_native h() # due to a failure to compile f() + @test g() == 0 +end + +module ReflectionTest +using Test, Random, InteractiveUtils + +function test_ast_reflection(freflect, f, types) + @test !isempty(freflect(f, types)) + nothing +end + +function test_bin_reflection(freflect, f, types) + iob = IOBuffer() + freflect(iob, f, types) + str = String(take!(iob)) + @test !isempty(str) + nothing +end + +function test_code_reflection(freflect, f, types, tester) + tester(freflect, f, types) + tester(freflect, f, (types.parameters...,)) + nothing +end + +function test_code_reflections(tester, freflect) + test_code_reflection(freflect, contains, + Tuple{AbstractString, Regex}, tester) # abstract type + test_code_reflection(freflect, +, Tuple{Int, Int}, tester) # leaftype signature + test_code_reflection(freflect, +, + Tuple{Array{Float32}, Array{Float32}}, tester) # incomplete types + test_code_reflection(freflect, Module, Tuple{}, tester) # Module() constructor (transforms to call) + test_code_reflection(freflect, Array{Int64}, Tuple{Array{Int32}}, tester) # with incomplete types + test_code_reflection(freflect, muladd, Tuple{Float64, Float64, Float64}, tester) +end + +test_code_reflections(test_bin_reflection, code_llvm) +test_code_reflections(test_bin_reflection, code_native) + +end # module ReflectionTest + +@test_throws ArgumentError("argument is not a generic function") code_llvm(===, Tuple{Int, Int}) +@test_throws ArgumentError("argument is not a generic function") code_native(===, Tuple{Int, Int}) + +# Issue #18883, code_llvm/code_native for generated functions +@generated f18883() = nothing +@test !isempty(sprint(code_llvm, f18883, Tuple{})) +@test !isempty(sprint(code_native, f18883, Tuple{})) diff --git a/stdlib/Mmap/test/runtests.jl b/stdlib/Mmap/test/runtests.jl index 0face3bf6fd551..c00c0d3b760576 100644 --- a/stdlib/Mmap/test/runtests.jl +++ b/stdlib/Mmap/test/runtests.jl @@ -194,22 +194,22 @@ close(s) s = open(file, "r") @test isreadonly(s) b = @inferred Mmap.mmap(s, BitArray, (17,13)) -@test Base._check_bitarray_consistency(b) +@test Test._check_bitarray_consistency(b) @test b == trues(17,13) @test_throws ArgumentError Mmap.mmap(s, BitArray, (7,3)) close(s) s = open(file, "r+") b = Mmap.mmap(s, BitArray, (17,19)) -@test Base._check_bitarray_consistency(b) +@test Test._check_bitarray_consistency(b) rand!(b) Mmap.sync!(b) b0 = copy(b) -@test Base._check_bitarray_consistency(b0) +@test Test._check_bitarray_consistency(b0) close(s) s = open(file, "r") @test isreadonly(s) b = Mmap.mmap(s, BitArray, (17,19)) -@test Base._check_bitarray_consistency(b) +@test Test._check_bitarray_consistency(b) @test b == b0 close(s) finalize(b); finalize(b0) @@ -223,7 +223,7 @@ end @test filesize(file) == 9 s = open(file, "r+") m = Mmap.mmap(s, BitArray, (72,)) -@test Base._check_bitarray_consistency(m) +@test Test._check_bitarray_consistency(m) @test length(m) == 72 close(s); finalize(m); m = nothing; GC.gc() rm(file) diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index 776a0d6c4ac16b..aee31cca60694b 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -9,6 +9,8 @@ import Base.Docs: doc, formatdoc, parsedoc, apropos using Base: with_output_color +using InteractiveUtils: subtypes + ## Help mode ## # This is split into helpmode and _helpmode to easier unittest _helpmode diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 517be6e9ec73df..edc604c723c596 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -1499,6 +1499,20 @@ guardsrand(f::Function, seed::Union{Vector{UInt32},Integer}) = guardsrand() do f() end +function _check_bitarray_consistency(B::BitArray{N}) where N + n = length(B) + if N ≠ 1 + all(d ≥ 0 for d in B.dims) || (@warn("Negative d in dims: $(B.dims)"); return false) + prod(B.dims) ≠ n && (@warn("Inconsistent dims/len: prod(dims)=$(prod(B.dims)) len=$n"); return false) + end + Bc = B.chunks + nc = length(Bc) + nc == num_bit_chunks(n) || (@warn("Incorrect chunks length for length $n: expected=$(num_bit_chunks(n)) actual=$nc"); return false) + n == 0 && return true + Bc[end] & _msk_end(n) == Bc[end] || (@warn("Nonzero bits in chunk after `BitArray` end"); return false) + return true +end + # 0.7 deprecations begin diff --git a/test/bitarray.jl b/test/bitarray.jl index c29347df29f2a7..0d2c31572641db 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license using Base: findprevnot, findnextnot -using Random, LinearAlgebra +using Random, LinearAlgebra, Test tc(r1::NTuple{N,Any}, r2::NTuple{N,Any}) where {N} = all(x->tc(x...), [zip(r1,r2)...]) tc(r1::BitArray{N}, r2::Union{BitArray{N},Array{Bool,N}}) where {N} = true @@ -9,7 +9,7 @@ tc(r1::Transpose{Bool,BitVector}, r2::Union{Transpose{Bool,BitVector},Transpose{ tc(r1::T, r2::T) where {T} = true tc(r1,r2) = false -bitcheck(b::BitArray) = Base._check_bitarray_consistency(b) +bitcheck(b::BitArray) = Test._check_bitarray_consistency(b) bitcheck(x) = true function check_bitop_call(ret_type, func, args...) diff --git a/test/compile.jl b/test/compile.jl index 96006e71c219a0..fcb4b9b2b7d224 100644 --- a/test/compile.jl +++ b/test/compile.jl @@ -133,7 +133,7 @@ try Base.convert(::Type{Ref}, ::Value18343{T}) where {T} = 3 - let some_method = @which Base.include("string") + let some_method = which(Base.include, (String,)) # global const some_method // FIXME: support for serializing a direct reference to an external Method not implemented global const some_linfo = ccall(:jl_specializations_get_linfo, Ref{Core.MethodInstance}, (Any, Any, Any, UInt), @@ -217,7 +217,7 @@ try [:Base64, :CRC32c, :Dates, :DelimitedFiles, :Distributed, :FileWatching, :Markdown, :Future, :IterativeEigensolvers, :Libdl, :LinearAlgebra, :Logging, :Mmap, :Printf, :Profile, :Random, :Serialization, :SharedArrays, :SparseArrays, :SuiteSparse, :Test, - :Unicode, :REPL])) + :Unicode, :REPL, :InteractiveUtils])) @test discard_module.(deps) == deps1 @test current_task()(0x01, 0x4000, 0x30031234) == 2 @@ -243,7 +243,7 @@ try Val{3}, Val{nothing}}, 0:25) - some_method = @which Base.include("string") + some_method = which(Base.include, (String,)) some_linfo = ccall(:jl_specializations_get_linfo, Ref{Core.MethodInstance}, (Any, Any, Any, UInt), some_method, Tuple{typeof(Base.include), String}, Core.svec(), typemax(UInt)) diff --git a/test/compiler/compiler.jl b/test/compiler/compiler.jl index df2c3944aca06e..74b77b840b504d 100644 --- a/test/compiler/compiler.jl +++ b/test/compiler/compiler.jl @@ -186,7 +186,7 @@ result_type9232(::Type{T1}, ::Type{T2}) where {T1<:Number,T2<:Number} = arithtyp # issue #10878 function g10878(x; kw...); end invoke_g10878() = invoke(g10878, Tuple{Any}, 1) -@code_typed invoke_g10878() +code_typed(invoke_g10878, ()) code_llvm(DevNull, invoke_g10878, ()) @@ -409,8 +409,8 @@ gpure(x::Irrational) = fpure(x) @test which(fpure, (typeof(pi),)).pure @test !which(gpure, ()).pure @test !which(gpure, (typeof(pi),)).pure -@test @code_typed(gpure())[1].pure -@test @code_typed(gpure(π))[1].pure +@test code_typed(gpure, ())[1].pure +@test code_typed(gpure, (typeof(π),))[1].pure @test gpure() == gpure() == gpure() @test gpure(π) == gpure(π) == gpure(π) @@ -488,10 +488,10 @@ function g19348(x) end for codetype in Any[ - @code_typed(f18679()), - @code_typed(g18679()), - @code_typed(h18679()), - @code_typed(g19348((1, 2.0)))] + code_typed(f18679, ())[1], + code_typed(g18679, ())[1], + code_typed(h18679, ())[1], + code_typed(g19348, (typeof((1, 2.0)),))[1]] # make sure none of the slottypes are left as Core.Compiler.Const objects code = codetype[1] @test all(x->isa(x, Type), code.slottypes) @@ -960,7 +960,7 @@ typeargs = (Type{Int},Type{Int},Type{Int},Type{Int},Type{Int},Type{Int}) # while doing constant propagation Base.@pure plus1(x) = x + 1 f21933(x::Val{T}) where {T} = f(Val(plus1(T))) -@code_typed f21933(Val(1)) +code_typed(f21933, (Val{1},)) Base.return_types(f21933, (Val{1},)) function count_specializations(method::Method) @@ -1097,14 +1097,14 @@ function sizeof_typeref(typeref) Core.sizeof(typeref[]) end @test @inferred(sizeof_typeref(Ref{DataType}(Int))) == sizeof(Int) -@test find_call(first(@code_typed sizeof_typeref(Ref{DataType}())).code, Core.sizeof, 2) +@test find_call(first(code_typed(sizeof_typeref, (Ref{DataType},))[1]).code, Core.sizeof, 2) # Constant `Vector` can be resized and shouldn't be optimized to a constant. const constvec = [1, 2, 3] @eval function sizeof_constvec() Core.sizeof($constvec) end @test @inferred(sizeof_constvec()) == sizeof(Int) * 3 -@test find_call(first(@code_typed sizeof_constvec()).code, Core.sizeof, 2) +@test find_call(first(code_typed(sizeof_constvec, ())[1]).code, Core.sizeof, 2) push!(constvec, 10) @test @inferred(sizeof_constvec()) == sizeof(Int) * 4 diff --git a/test/compiler/validation.jl b/test/compiler/validation.jl index 962e2d75b61dc9..d7a375e70ea030 100644 --- a/test/compiler/validation.jl +++ b/test/compiler/validation.jl @@ -82,7 +82,7 @@ end end @testset "SLOTTYPES_MISMATCH" begin - c = @code_typed(f22938(1,2,3,4))[1] + c = code_typed(f22938, (Int,Int,Int,Int))[1] pop!(c.slottypes) errors = Core.Compiler.validate_code(c) @test length(errors) == 1 @@ -98,7 +98,7 @@ end end @testset "SSAVALUETYPES_MISMATCH" begin - c = @code_typed(f22938(1,2,3,4))[1] + c = code_typed(f22938, (Int,Int,Int,Int))[1] empty!(c.ssavaluetypes) errors = Core.Compiler.validate_code(c) @test length(errors) == 1 diff --git a/test/core.jl b/test/core.jl index 91a61a6ecac2e1..520a5bfb796c6e 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2554,6 +2554,8 @@ const N10281 = 1000 end end === nothing +import InteractiveUtils.versioninfo + # issue #10221 module GCbrokentype OLD_STDOUT = STDOUT @@ -4788,14 +4790,6 @@ function f18173() end @test f18173() == false -let _true = Ref(true), f, g, h - @noinline f() = ccall((:time, "error_library_doesnt_exist\0"), Cvoid, ()) # some expression that throws an error in codegen - @noinline g() = _true[] ? 0 : h() - @noinline h() = (g(); f()) - @test_throws ErrorException @code_native h() # due to a failure to compile f() - @test g() == 0 -end - fVararg(x) = Vararg{x} gVararg(a::fVararg(Int)) = length(a) @test gVararg(1,2,3,4,5) == 5 @@ -4862,7 +4856,7 @@ GC.enable(true) # issue #18710 bad_tvars() where {T} = 1 -@test isa(@which(bad_tvars()), Method) +@test isa(which(bad_tvars, ()), Method) @test bad_tvars() === 1 bad_tvars2() where {T} = T @test_throws UndefVarError(:T) bad_tvars2() @@ -4957,12 +4951,12 @@ function let_Box5() return g end end -@test any(contains_Box, (@code_lowered let_Box1()).code) -@test any(contains_Box, (@code_lowered let_Box2()).code) -@test any(contains_Box, (@code_lowered let_Box3()).code) -@test any(contains_Box, (@code_lowered let_Box4()).code) -@test any(contains_Box, (@code_lowered let_Box5()).code) -@test !any(contains_Box, (@code_lowered let_noBox()).code) +@test any(contains_Box, code_lowered(let_Box1,())[1].code) +@test any(contains_Box, code_lowered(let_Box2,())[1].code) +@test any(contains_Box, code_lowered(let_Box3,())[1].code) +@test any(contains_Box, code_lowered(let_Box4,())[1].code) +@test any(contains_Box, code_lowered(let_Box5,())[1].code) +@test !any(contains_Box, code_lowered(let_noBox,())[1].code) @test let_Box1()() == 22 @test let_Box2()() == 23 @test let_Box3()() == 24 @@ -5538,7 +5532,7 @@ module GlobalDef18933 global sincos nothing end - @test @which(sincos) === Base.Math + @test which(:sincos) === Base.Math @test @isdefined sincos @test sincos === Base.sincos end diff --git a/test/docs.jl b/test/docs.jl index 2d6edef57dc11a..0494accb96caf4 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -6,6 +6,7 @@ using Markdown using REPL using REPL: @repl, repl_latex, _repl, accessible +using InteractiveUtils: apropos # For curmod_* include("testenv.jl") diff --git a/test/misc.jl b/test/misc.jl index 69906755c680b1..0766b16ca567ae 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -90,16 +90,6 @@ end @test GC.enable(true) == false @test GC.enable(true) -# test methodswith -# `methodswith` relies on exported symbols -export func4union, Base -struct NoMethodHasThisType end -@test isempty(methodswith(NoMethodHasThisType)) -@test !isempty(methodswith(Int)) -struct Type4Union end -func4union(::Union{Type4Union,Int}) = () -@test !isempty(methodswith(Type4Union, @__MODULE__)) - # PR #10984 # Disable on windows because of issue (missing flush) when redirecting STDERR. let @@ -202,18 +192,6 @@ let A = zeros(1000), B = reshape(A, (1,1000)) @test summarysize(A) > sizeof(A) end -module _test_varinfo_ -export x -x = 1.0 -end -@test repr(varinfo(Main, r"^$")) == """ -| name | size | summary | -|:---- | ----:|:------- | -""" -let v = repr(varinfo(_test_varinfo_)) - @test contains(v, "| x | 8 bytes | Float64 |") -end - # issue #13021 let ex = try Main.x13021 = 0 @@ -224,16 +202,6 @@ end @test isa(ex, ErrorException) && ex.msg == "cannot assign variables in other modules" end -# Issue 14173 -module Tmp14173 - using Random - export A - A = randn(2000, 2000) -end -varinfo(Tmp14173) # warm up -const MEMDEBUG = ccall(:jl_is_memdebug, Bool, ()) -@test @allocated(varinfo(Tmp14173)) < (MEMDEBUG ? 60000 : 20000) - ## test conversion from UTF-8 to UTF-16 (for Windows APIs) # empty arrays @@ -641,9 +609,3 @@ end # PR #23664, make sure names don't get added to the default `Main` workspace @test readlines(`$(Base.julia_cmd()) --startup-file=no -e 'foreach(println, names(Main))'`) == ["Base","Core","Main"] - -# PR #24997: test that `varinfo` doesn't fail when encountering `missing` -module A - export missing - varinfo(A) -end diff --git a/test/parse.jl b/test/parse.jl index 47f862bcc2624b..5bfc14653f8714 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -27,7 +27,7 @@ @test parse(Int,'3', base = 8) == 3 # Issue 20587 -for T in vcat(subtypes(Signed), subtypes(Unsigned)) +for T in Any[BigInt, Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8] T === BigInt && continue # TODO: make BigInt pass this test for s in ["", " ", " "] # Without a base (handles things like "0x00001111", etc) diff --git a/test/reflection.jl b/test/reflection.jl index a1a67ba5b65933..e8550ce0596119 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -39,108 +39,9 @@ end test_code_reflections(test_ast_reflection, code_lowered) test_code_reflections(test_ast_reflection, code_typed) -test_code_reflections(test_bin_reflection, code_llvm) -test_code_reflections(test_bin_reflection, code_native) - -# Issue #16326 -mktemp() do f, io - OLDSTDOUT = STDOUT - redirect_stdout(io) - @test try @code_native map(abs, rand(3)); true; catch; false; end - redirect_stdout(OLDSTDOUT) - nothing -end end # module ReflectionTest -# code_warntype -module WarnType -using Test, Random - -function warntype_hastag(f, types, tag) - iob = IOBuffer() - code_warntype(iob, f, types) - str = String(take!(iob)) - return contains(str, tag) -end - -pos_stable(x) = x > 0 ? x : zero(x) -pos_unstable(x) = x > 0 ? x : 0 - -tag = "UNION" -@test warntype_hastag(pos_unstable, Tuple{Float64}, tag) -@test !warntype_hastag(pos_stable, Tuple{Float64}, tag) - -mutable struct Stable{T,N} - A::Array{T,N} -end -mutable struct Unstable{T} - A::Array{T} -end -Base.getindex(A::Stable, i) = A.A[i] -Base.getindex(A::Unstable, i) = A.A[i] - -tag = "ARRAY{FLOAT64,N}" -@test warntype_hastag(getindex, Tuple{Unstable{Float64},Int}, tag) -@test !warntype_hastag(getindex, Tuple{Stable{Float64,2},Int}, tag) -@test warntype_hastag(getindex, Tuple{Stable{Float64},Int}, tag) - -# Make sure emphasis is not used for other functions -tag = "ANY" -iob = IOBuffer() -show(iob, Meta.lower(Main, :(x -> x^2))) -str = String(take!(iob)) -@test !contains(str, tag) - -# Make sure non used variables are not emphasized -has_unused() = (a = rand(5)) -@test !warntype_hastag(has_unused, Tuple{}, tag) -@test warntype_hastag(has_unused, Tuple{}, "") - -# Make sure that "expected" unions are highlighted with warning color instead of error color -iob = IOBuffer() -code_warntype(IOContext(iob, :color => true), x -> (x > 1 ? "foo" : nothing), Tuple{Int64}) -str = String(take!(iob)) -@test contains(str, Base.text_colors[Base.warn_color()]) - -# Make sure getproperty and setproperty! works with @code_... macros -struct T1234321 - t::Int -end -Base.getproperty(t::T1234321, ::Symbol) = "foo" -@test (@code_typed T1234321(1).f).second == String -Base.setproperty!(t::T1234321, ::Symbol, ::Symbol) = "foo" -@test (@code_typed T1234321(1).f = :foo).second == String - -module ImportIntrinsics15819 -# Make sure changing the lookup path of an intrinsic doesn't break -# the heuristic for type instability warning. -import Core.Intrinsics: sqrt_llvm, bitcast -# Use import -sqrt15819(x::Float64) = bitcast(Float64, sqrt_llvm(x)) -# Use fully qualified name -sqrt15819(x::Float32) = bitcast(Float32, Core.Intrinsics.sqrt_llvm(x)) -end # module ImportIntrinsics15819 - -foo11122(x) = @fastmath x - 1.0 - -# issue #11122, #13568 and #15819 -@test !warntype_hastag(+, Tuple{Int,Int}, tag) -@test !warntype_hastag(-, Tuple{Int,Int}, tag) -@test !warntype_hastag(*, Tuple{Int,Int}, tag) -@test !warntype_hastag(/, Tuple{Int,Int}, tag) -@test !warntype_hastag(foo11122, Tuple{Float32}, tag) -@test !warntype_hastag(foo11122, Tuple{Float64}, tag) -@test !warntype_hastag(foo11122, Tuple{Int}, tag) -@test !warntype_hastag(sqrt, Tuple{Int}, tag) -@test !warntype_hastag(sqrt, Tuple{Float64}, tag) -@test !warntype_hastag(^, Tuple{Float64,Int32}, tag) -@test !warntype_hastag(^, Tuple{Float32,Int32}, tag) -@test !warntype_hastag(ImportIntrinsics15819.sqrt15819, Tuple{Float64}, tag) -@test !warntype_hastag(ImportIntrinsics15819.sqrt15819, Tuple{Float32}, tag) - -end # module WarnType - # isbits @test !isbits(Array{Int}) @@ -275,15 +176,13 @@ let @test parentmodule(Foo7648) == TestMod7648 @test nameof(Foo7648) == :Foo7648 @test basename(functionloc(foo7648, (Any,))[1]) == "reflection.jl" - @test first(methods(TestMod7648.TestModSub9475.foo7648)) == @which foo7648(5) - @test TestMod7648 == @which foo7648 - @test TestMod7648.TestModSub9475 == @which a9475 + @test first(methods(TestMod7648.TestModSub9475.foo7648)) == which(foo7648, (Int,)) + @test TestMod7648 == which(@__MODULE__, :foo7648) + @test TestMod7648.TestModSub9475 == which(@__MODULE__, :a9475) end @test_throws ArgumentError("argument is not a generic function") which(===, Tuple{Int, Int}) @test_throws ArgumentError("argument is not a generic function") code_typed(===, Tuple{Int, Int}) -@test_throws ArgumentError("argument is not a generic function") code_llvm(===, Tuple{Int, Int}) -@test_throws ArgumentError("argument is not a generic function") code_native(===, Tuple{Int, Int}) @test_throws ArgumentError("argument is not a generic function") Base.return_types(===, Tuple{Int, Int}) module TestingExported @@ -292,32 +191,17 @@ include("testenv.jl") # for curmod_str import Base.isexported global this_is_not_defined export this_is_not_defined -@test_throws ErrorException("\"this_is_not_defined\" is not defined in module Main") which(:this_is_not_defined) -@test_throws ErrorException("\"this_is_not_defined\" is not defined in module $curmod_str") @which this_is_not_defined -@test_throws ErrorException("\"this_is_not_exported\" is not defined in module Main") which(:this_is_not_exported) +@test_throws ErrorException("\"this_is_not_defined\" is not defined in module Main") which(Main, :this_is_not_defined) +@test_throws ErrorException("\"this_is_not_exported\" is not defined in module Main") which(Main, :this_is_not_exported) @test isexported(@__MODULE__, :this_is_not_defined) @test !isexported(@__MODULE__, :this_is_not_exported) const a_value = 1 -@test Base.which_module(@__MODULE__, :a_value) === @__MODULE__ -@test @which(a_value) === @__MODULE__ -@test_throws ErrorException("\"a_value\" is not defined in module Main") which(:a_value) -@test which(:Core) === Main +@test which(@__MODULE__, :a_value) === @__MODULE__ +@test_throws ErrorException("\"a_value\" is not defined in module Main") which(Main, :a_value) +@test which(Main, :Core) === Main @test !isexported(@__MODULE__, :a_value) end -# issue #13264 -@test isa((@which vcat(1...)), Method) - -# issue #13464 -let t13464 = "hey there sailor" - try - @which t13464[1,1] = (1.0,true) - error("unexpected") - catch err13464 - @test startswith(err13464.msg, "expression is not a function call, or is too complex") - end -end - # PR 13825 let ex = :(a + b) @test string(ex) == "a + b" @@ -395,25 +279,6 @@ for (f, t) in Any[(definitely_not_in_sysimg, Tuple{}), @test ccall(:jl_get_llvm_fptr, Ptr{Cvoid}, (Ptr{Cvoid},), llvmf) != C_NULL end -module MacroTest -export @macrotest -macro macrotest(x::Int, y::Symbol) end -macro macrotest(x::Int, y::Int) - nothing #This is here because of #15280 -end -end - -let - using .MacroTest - a = 1 - m = getfield(@__MODULE__, Symbol("@macrotest")) - @test which(m, Tuple{LineNumberNode, Module, Int, Symbol}) == @which @macrotest 1 a - @test which(m, Tuple{LineNumberNode, Module, Int, Int}) == @which @macrotest 1 1 - - @test first(methods(m, Tuple{LineNumberNode, Module, Int, Int})) == @which MacroTest.@macrotest 1 1 - @test functionloc(@which @macrotest 1 1) == @functionloc @macrotest 1 1 -end - # issue #15714 # show variable names for slots and suppress spurious type warnings function f15714(array_var15714) @@ -440,6 +305,8 @@ function g15714(array_var15714) end end +import InteractiveUtils.code_warntype + used_dup_var_tested15714 = false used_unique_var_tested15714 = false function test_typed_ast_printing(Base.@nospecialize(f), Base.@nospecialize(types), must_used_vars) @@ -617,42 +484,6 @@ else @test h16850 === nothing end -# Adds test for PR #17636 -let a = @code_typed 1 + 1 - b = @code_lowered 1 + 1 - @test isa(a, Pair{CodeInfo, DataType}) - @test isa(b, CodeInfo) - @test isa(a[1].code, Array{Any,1}) - @test isa(b.code, Array{Any,1}) - - function thing(a::Array, b::Real) - println("thing") - end - function thing(a::AbstractArray, b::Int) - println("blah") - end - @test_throws MethodError thing(rand(10), 1) - a = @code_typed thing(rand(10), 1) - b = @code_lowered thing(rand(10), 1) - @test length(a) == 0 - @test length(b) == 0 -end - -mutable struct A18434 -end -A18434(x; y=1) = 1 - -global counter18434 = 0 -function get_A18434() - global counter18434 - counter18434 += 1 - return A18434 -end -@which get_A18434()(1; y=2) -@test counter18434 == 1 -@which get_A18434()(1, y=2) -@test counter18434 == 2 - # PR #18888: code_typed shouldn't cache if not optimizing let world = typemax(UInt) @@ -671,14 +502,6 @@ let @test isdefined(code, :inferred) end -# Issue #18883, code_llvm/code_native for generated functions -@generated f18883() = nothing -@test !isempty(sprint(code_llvm, f18883, Tuple{})) -@test !isempty(sprint(code_native, f18883, Tuple{})) - -# PR #19964 -@test isempty(subtypes(Float64)) - # New reflection methods in 0.6 struct ReflectionExample{T<:AbstractFloat, N} x::Tuple{T, N} @@ -723,14 +546,6 @@ let Base.typename(Union{Int, Float64})) end -# Issue #20086 -abstract type A20086{T,N} end -struct B20086{T,N} <: A20086{T,N} end -@test subtypes(A20086) == [B20086] -@test subtypes(A20086{Int}) == [B20086{Int}] -@test subtypes(A20086{T,3} where T) == [B20086{T,3} where T] -@test subtypes(A20086{Int,3}) == [B20086{Int,3}] - # sizeof and nfields @test sizeof(Int16) == 2 @test sizeof(ComplexF64) == 16 @@ -789,7 +604,7 @@ instance = Core.Compiler.code_for_method(m, mtypes, msp, world, false) cinfo_generated = Core.Compiler.get_staged(instance) @test_throws ErrorException Base.uncompressed_ast(m) -test_similar_codeinfo(@code_lowered(f22979(x22979...)), cinfo_generated) +test_similar_codeinfo(code_lowered(f22979, typeof(x22979))[1], cinfo_generated) cinfos = code_lowered(f22979, typeof.(x22979), generated = true) @test length(cinfos) == 1 diff --git a/test/show.jl b/test/show.jl index 372108287f984f..6a2af274ec9b2e 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1057,7 +1057,7 @@ end end @testset "Methods" begin - m = @which sin(1.0) + m = which(sin, (Float64,)) io = IOBuffer() show(io, "text/html", m) s = String(take!(io)) diff --git a/test/syntax.jl b/test/syntax.jl index 2e5e3c3ab43cac..745f7526494bb7 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -750,8 +750,8 @@ end end end -f1_exprs = get_expr_list(@code_typed(f1(1))[1]) -f2_exprs = get_expr_list(@code_typed(f2(1))[1]) +f1_exprs = get_expr_list(code_typed(f1, (Int,))[1]) +f2_exprs = get_expr_list(code_typed(f2, (Int,))[1]) @test Meta.isexpr(f1_exprs[end], :return) @test is_pop_loc(f2_exprs[end]) diff --git a/test/version.jl b/test/version.jl index 13d6309e15364c..aa2a8f820a9e67 100644 --- a/test/version.jl +++ b/test/version.jl @@ -281,21 +281,3 @@ for t = 1:1_000 @test (v ∈ a && v ∈ b) ? (v ∈ i) : (v ∉ i) end end - -# PR #23075 -@testset "versioninfo" begin - # check that versioninfo(io; verbose=true) doesn't error, produces some output - # and doesn't invoke Pkg.status which will error if JULIA_PKGDIR is set - mktempdir() do dir - withenv("JULIA_PKGDIR" => dir) do - buf = PipeBuffer() - versioninfo(buf, verbose=true) - ver = read(buf, String) - @test startswith(ver, "Julia Version $VERSION") - @test contains(ver, "Environment:") - @test contains(ver, "Package Status:") - @test contains(ver, "no packages installed") - @test isempty(readdir(dir)) - end - end -end