Revision 3711749292ba9c29ad2e3b9eaee90995f8c8290a authored by Keno Fischer on 11 October 2023, 14:41:22 UTC, committed by GitHub on 11 October 2023, 14:41:22 UTC
This should be NFC and is intended to allow the optimizer to delete
:enter statements (by replacing them with `nothing`), without leaving
dangling `:leave`s around. This is accomplished by having `leave` take
(a variable number of) `:enter` tokens (that are already being used by
`:pop_exception`). The semantics are that a literal `nothing` or an
SSAValue pointing to a `nothing` statement are ignored, and one
exception handler is popped for each remaining argument. The actual
value of the token is ignored, except that the verifier asserts that it
belongs to an `:enter`.

Note that we don't need to do the same for :pop_exception, because the
token generated by an `:enter` is semantically only in scope for
:pop_exception during its catch block. If we determine the `:enter` is
dead, then its catch block is guaranteed to not be executed and will be
deleted wholesale by cfg liveness.

I was considering doing something fancier where :leave is changed back
to taking an integer after optimization, but the case where the IR size
is bigger after this change (when we are `:leave`ing many handlers) is
fairly rare and likely not worth the additional complexity or time cost
to do anything special. If it does show up in size benchmarks, I'd
rather give `:leave` a special, compact encoding.
1 parent 8180240
Raw File
multimedia.jl
# This file is a part of Julia. License is MIT: https://julialang.org/license

module Multimedia

import .Base: show, print, convert, repr

export AbstractDisplay, display, pushdisplay, popdisplay, displayable, redisplay,
    MIME, @MIME_str, istextmime,
    showable, TextDisplay

###########################################################################
# We define a singleton type MIME{mime symbol} for each MIME type, so
# that Julia's dispatch and overloading mechanisms can be used to
# dispatch show and to add conversions for new types.

"""
    MIME

A type representing a standard internet data format. "MIME" stands for
"Multipurpose Internet Mail Extensions", since the standard was originally
used to describe multimedia attachments to email messages.

A `MIME` object can be passed as the second argument to [`show`](@ref) to
request output in that format.

# Examples
```jldoctest
julia> show(stdout, MIME("text/plain"), "hi")
"hi"
```
"""
struct MIME{mime} end

"""
    @MIME_str

A convenience macro for writing [`MIME`](@ref) types, typically used when
adding methods to [`show`](@ref).
For example the syntax `show(io::IO, ::MIME"text/html", x::MyType) = ...`
could be used to define how to write an HTML representation of `MyType`.
"""
macro MIME_str(s)
    :(MIME{$(Expr(:quote, Symbol(s)))})
end

# fallback text/plain representation of any type:
show(io::IO, ::MIME"text/plain", x) = show(io, x)

MIME(s) = MIME{Symbol(s)}()
show(io::IO, ::MIME{mime}) where {mime} = print(io, "MIME type ", string(mime))
print(io::IO, ::MIME{mime}) where {mime} = print(io, mime)

###########################################################################
# For any type T one can define show(io, ::MIME"type", x::T) = ...
# in order to provide a way to export T as a given mime type.

"""
    showable(mime, x)

Return a boolean value indicating whether or not the object `x` can be written
as the given `mime` type.

(By default, this is determined automatically by the existence of the
corresponding [`show`](@ref) method for `typeof(x)`.  Some types provide custom `showable`
methods; for example, if the available MIME formats depend on the *value* of `x`.)

# Examples
```jldoctest
julia> showable(MIME("text/plain"), rand(5))
true

julia> showable("image/png", rand(5))
false
```
"""
showable(::MIME{mime}, @nospecialize x) where {mime} = hasmethod(show, Tuple{IO, MIME{mime}, typeof(x)})
showable(m::AbstractString, @nospecialize x) = showable(MIME(m), x)

"""
    show(io::IO, mime, x)

The [`display`](@ref) functions ultimately call `show` in order to write an object `x` as a
given `mime` type to a given I/O stream `io` (usually a memory buffer), if possible. In order
to provide a rich multimedia representation of a user-defined type `T`, it is only necessary
to define a new `show` method for `T`, via: `show(io, ::MIME"mime", x::T) = ...`,
where `mime` is a MIME-type string and the function body calls [`write`](@ref) (or similar) to write
that representation of `x` to `io`. (Note that the `MIME""` notation only supports
literal strings; to construct `MIME` types in a more flexible manner use
`MIME{Symbol("")}`.)

For example, if you define a `MyImage` type and know how to write it to a PNG file, you
could define a function `show(io, ::MIME"image/png", x::MyImage) = ...` to allow
your images to be displayed on any PNG-capable `AbstractDisplay` (such as IJulia). As usual, be sure
to `import Base.show` in order to add new methods to the built-in Julia function
`show`.

Technically, the `MIME"mime"` macro defines a singleton type for the given `mime` string,
which allows us to exploit Julia's dispatch mechanisms in determining how to display objects
of any given type.

The default MIME type is `MIME"text/plain"`. There is a fallback definition for `text/plain`
output that calls `show` with 2 arguments, so it is not always necessary to add a method
for that case. If a type benefits from custom human-readable output though,
`show(::IO, ::MIME"text/plain", ::T)` should be defined. For example, the `Day` type uses
`1 day` as the output for the `text/plain` MIME type, and `Day(1)` as the output of 2-argument `show`.

# Examples
```jldoctest
julia> struct Day
           n::Int
       end

julia> Base.show(io::IO, ::MIME"text/plain", d::Day) = print(io, d.n, " day")

julia> Day(1)
1 day
```

Container types generally implement 3-argument `show` by calling `show(io, MIME"text/plain"(), x)`
for elements `x`, with `:compact => true` set in an [`IOContext`](@ref) passed as the first argument.
"""
show(stream::IO, mime, x)
show(io::IO, m::AbstractString, x) = show(io, MIME(m), x)

"""
    repr(mime, x; context=nothing)

Return an `AbstractString` or `Vector{UInt8}` containing the representation of
`x` in the requested `mime` type, as written by [`show(io, mime, x)`](@ref) (throwing a
[`MethodError`](@ref) if no appropriate `show` is available). An `AbstractString` is
returned for MIME types with textual representations (such as `"text/html"` or
`"application/postscript"`), whereas binary data is returned as
`Vector{UInt8}`. (The function `istextmime(mime)` returns whether or not Julia
treats a given `mime` type as text.)

The optional keyword argument `context` can be set to `:key=>value` pair
or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O
stream passed to `show`.

As a special case, if `x` is an `AbstractString` (for textual MIME types) or a
`Vector{UInt8}` (for binary MIME types), the `repr` function assumes that
`x` is already in the requested `mime` format and simply returns `x`. This
special case does not apply to the `"text/plain"` MIME type. This is useful so
that raw data can be passed to `display(m::MIME, x)`.

In particular, `repr("text/plain", x)` is typically a "pretty-printed" version
of `x` designed for human consumption.  See also [`repr(x)`](@ref) to instead
return a string corresponding to [`show(x)`](@ref) that may be closer to how
the value of `x` would be entered in Julia.

# Examples
```jldoctest
julia> A = [1 2; 3 4];

julia> repr("text/plain", A)
"2×2 Matrix{Int64}:\\n 1  2\\n 3  4"
```
"""
repr(m::MIME, x; context=nothing) = istextmime(m) ? _textrepr(m, x, context) : _binrepr(m, x, context)
repr(m::AbstractString, x; context=nothing) = repr(MIME(m), x; context=context)

# strings are shown escaped for text/plain
_textrepr(m::MIME, x, context) = String(__binrepr(m, x, context))
_textrepr(::MIME, x::AbstractString, context) = x
_textrepr(m::MIME"text/plain", x::AbstractString, context) = String(__binrepr(m, x, context))


function __binrepr(m::MIME, x, context)
    s = IOBuffer()
    if context === nothing
        show(s, m, x)
    else
        show(IOContext(s, context), m, x)
    end
    take!(s)
end
_binrepr(m::MIME, x, context) = __binrepr(m, x, context)
_binrepr(m::MIME, x::Vector{UInt8}, context) = x

"""
    istextmime(m::MIME)

Determine whether a MIME type is text data. MIME types are assumed to be binary
data except for a set of types known to be text data (possibly Unicode).

# Examples
```jldoctest
julia> istextmime(MIME("text/plain"))
true

julia> istextmime(MIME("image/png"))
false
```
"""
istextmime(m::MIME) = startswith(string(m), "text/")
istextmime(m::AbstractString) = istextmime(MIME(m))

for mime in ["application/atom+xml", "application/ecmascript",
             "application/javascript", "application/julia",
             "application/json", "application/postscript",
             "application/rdf+xml", "application/rss+xml",
             "application/x-latex", "application/xhtml+xml", "application/xml",
             "application/xml-dtd", "image/svg+xml", "model/vrml",
             "model/x3d+vrml", "model/x3d+xml"]
    global istextmime(::MIME{Symbol(mime)}) = true
end

###########################################################################
# We have an abstract AbstractDisplay class that can be subclassed in order to
# define new rich-display output devices.  A typical subclass should
# overload display(d::AbstractDisplay, m::MIME, x) for supported MIME types m,
# (typically using show, repr, ..., to get the MIME
# representation of x) and should also overload display(d::AbstractDisplay, x)
# to display x in whatever MIME type is preferred by the AbstractDisplay and
# is writable by x.  display(..., x) should throw a MethodError if x
# cannot be displayed.  The return value of display(...) is up to the
# AbstractDisplay type.

"""
    AbstractDisplay

Abstract supertype for rich display output devices. [`TextDisplay`](@ref) is a subtype
of this.
"""
abstract type AbstractDisplay end

# it is convenient to accept strings instead of ::MIME
display(d::AbstractDisplay, mime::AbstractString, @nospecialize x) = display(d, MIME(mime), x)
display(mime::AbstractString, @nospecialize x) = display(MIME(mime), x)

"""
    displayable(mime) -> Bool
    displayable(d::AbstractDisplay, mime) -> Bool

Return a boolean value indicating whether the given `mime` type (string) is displayable by
any of the displays in the current display stack, or specifically by the display `d` in the
second variant.
"""
displayable(d::AbstractDisplay, mime::AbstractString) = displayable(d, MIME(mime))
displayable(mime::AbstractString) = displayable(MIME(mime))

# simplest display, which only knows how to display text/plain

"""
    TextDisplay(io::IO)

Return a `TextDisplay <: AbstractDisplay`, which displays any object as the text/plain MIME type
(by default), writing the text representation to the given I/O stream. (This is how
objects are printed in the Julia REPL.)
"""
struct TextDisplay <: AbstractDisplay
    io::IO
end
display(d::TextDisplay, M::MIME"text/plain", @nospecialize x) = (show(d.io, M, x); println(d.io))
display(d::TextDisplay, @nospecialize x) = display(d, MIME"text/plain"(), x)

# if you explicitly call display("text/foo", x), it should work on a TextDisplay:
displayable(d::TextDisplay, M::MIME) = istextmime(M)
function display(d::TextDisplay, M::MIME, @nospecialize x)
    displayable(d, M) || throw(MethodError(display, (d, M, x)))
    show(d.io, M, x); println(d.io)
end

import Base: close, flush
flush(d::TextDisplay) = flush(d.io)
close(d::TextDisplay) = close(d.io)

###########################################################################
# We keep a stack of Displays, and calling display(x) uses the topmost
# AbstractDisplay that is capable of displaying x (doesn't throw an error)

const displays = AbstractDisplay[]

"""
    pushdisplay(d::AbstractDisplay)

Pushes a new display `d` on top of the global display-backend stack. Calling `display(x)` or
`display(mime, x)` will display `x` on the topmost compatible backend in the stack (i.e.,
the topmost backend that does not throw a [`MethodError`](@ref)).
"""
function pushdisplay(d::AbstractDisplay)
    global displays
    push!(displays, d)
end

"""
    popdisplay()
    popdisplay(d::AbstractDisplay)

Pop the topmost backend off of the display-backend stack, or the topmost copy of `d` in the
second variant.
"""
popdisplay() = pop!(displays)
function popdisplay(d::AbstractDisplay)
    for i = length(displays):-1:1
        if d == displays[i]
            return splice!(displays, i)
        end
    end
    throw(KeyError(d))
end
function reinit_displays()
    empty!(displays)
    pushdisplay(TextDisplay(stdout))
end

xdisplayable(D::AbstractDisplay, @nospecialize args...) = applicable(display, D, args...)

"""
    display(x)
    display(d::AbstractDisplay, x)
    display(mime, x)
    display(d::AbstractDisplay, mime, x)

Display `x` using the topmost applicable display in the display stack, typically using the
richest supported multimedia output for `x`, with plain-text [`stdout`](@ref) output as a fallback.
The `display(d, x)` variant attempts to display `x` on the given display `d` only, throwing
a [`MethodError`](@ref) if `d` cannot display objects of this type.

In general, you cannot assume that `display` output goes to `stdout` (unlike [`print(x)`](@ref) or
[`show(x)`](@ref)).  For example, `display(x)` may open up a separate window with an image.
`display(x)` means "show `x` in the best way you can for the current output device(s)."
If you want REPL-like text output that is guaranteed to go to `stdout`, use
[`show(stdout, "text/plain", x)`](@ref) instead.

There are also two variants with a `mime` argument (a MIME type string, such as
`"image/png"`), which attempt to display `x` using the requested MIME type *only*, throwing
a `MethodError` if this type is not supported by either the display(s) or by `x`. With these
variants, one can also supply the "raw" data in the requested MIME type by passing
`x::AbstractString` (for MIME types with text-based storage, such as text/html or
application/postscript) or `x::Vector{UInt8}` (for binary MIME types).

To customize how instances of a type are displayed, overload [`show`](@ref) rather than `display`,
as explained in the manual section on [custom pretty-printing](@ref man-custom-pretty-printing).
"""
function display(@nospecialize x)
    for i = length(displays):-1:1
        if xdisplayable(displays[i], x)
            try
                return display(displays[i], x)
            catch e
                isa(e, MethodError) && (e.f === display || e.f === show) ||
                    rethrow()
            end
        end
    end
    throw(MethodError(display, (x,)))
end

function display(m::MIME, @nospecialize x)
    for i = length(displays):-1:1
        if xdisplayable(displays[i], m, x)
            try
                return display(displays[i], m, x)
            catch e
                isa(e, MethodError) && e.f == display ||
                    rethrow()
            end
        end
    end
    throw(MethodError(display, (m, x)))
end

displayable(d::D, ::MIME{mime}) where {D<:AbstractDisplay,mime} =
    hasmethod(display, Tuple{D,MIME{mime},Any})

function displayable(m::MIME)
    for d in displays
        displayable(d, m) && return true
    end
    return false
end

###########################################################################
# The redisplay method can be overridden by a AbstractDisplay in order to
# update an existing display (instead of, for example, opening a new
# window), and is used by the IJulia interface to defer display
# until the next interactive prompt.  This is especially useful
# for Matlab/Pylab-like stateful plotting interfaces, where
# a plot is created and then modified many times (xlabel, title, etc.).

"""
    redisplay(x)
    redisplay(d::AbstractDisplay, x)
    redisplay(mime, x)
    redisplay(d::AbstractDisplay, mime, x)

By default, the `redisplay` functions simply call [`display`](@ref).
However, some display backends may override `redisplay` to modify an existing
display of `x` (if any).
Using `redisplay` is also a hint to the backend that `x` may be redisplayed
several times, and the backend may choose to defer the display until
(for example) the next interactive prompt.
"""
function redisplay(@nospecialize x)
    for i = length(displays):-1:1
        if xdisplayable(displays[i], x)
            try
                return redisplay(displays[i], x)
            catch e
                isa(e, MethodError) && e.f in (redisplay, display, show) ||
                    rethrow()
            end
        end
    end
    throw(MethodError(redisplay, (x,)))
end

function redisplay(m::Union{MIME,AbstractString}, @nospecialize x)
    for i = length(displays):-1:1
        if xdisplayable(displays[i], m, x)
            try
                return redisplay(displays[i], m, x)
            catch e
                isa(e, MethodError) && e.f in (redisplay, display) ||
                    rethrow()
            end
        end
    end
    throw(MethodError(redisplay, (m, x)))
end

# default redisplay is simply to call display
redisplay(d::AbstractDisplay, @nospecialize x) = display(d, x)
redisplay(d::AbstractDisplay, m::Union{MIME,AbstractString}, @nospecialize x) = display(d, m, x)

###########################################################################

end # module
back to top