From 4337bb8bfe60dd86dd2366a6f05b537110f030d5 Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Fri, 4 Apr 2014 12:48:47 -0400 Subject: [PATCH] Add edit, less and code_* macros, mirroring which The macros are programmatically generated, based on the previous macro for which. The arguments are evaluated and their types are determined so the corresponding function may be called. Adds news and documentation. --- NEWS.md | 3 +++ base/deprecated.jl | 2 ++ base/exports.jl | 6 +++++ base/inference.jl | 2 +- base/interactiveutil.jl | 56 +++++++++++++++++++++++++---------------- base/reflection.jl | 10 ++++---- doc/stdlib/base.rst | 28 +++++++++++++++++++-- 7 files changed, 78 insertions(+), 29 deletions(-) diff --git a/NEWS.md b/NEWS.md index 2c2e1617addea..42f8a134d4538 100644 --- a/NEWS.md +++ b/NEWS.md @@ -49,6 +49,8 @@ Library improvements * New functions `minmax` and `extrema` ([#5275]). + * New macros `@edit`, `@less`, `@code_typed`, `@code_lowered`, `@code_llvm` and `@code_native` that all function like `@which` ([#5832]). + * `consume(p)` extended to `consume(p, args...)`, allowing it to optionally pass `args...` back to the producer ([#4775]). @@ -756,6 +758,7 @@ Too numerous to mention. [#3605]: https://github.com/JuliaLang/julia/pull/3605 [#3233]: https://github.com/JuliaLang/julia/pull/3233 [#4811]: https://github.com/JuliaLang/julia/pull/4811 +[#5832]: https://github.com/JuliaLang/julia/pull/5832 [packages chapter]: http://docs.julialang.org/en/latest/manual/packages/ [sorting functions]: http://docs.julialang.org/en/latest/stdlib/sort/ diff --git a/base/deprecated.jl b/base/deprecated.jl index a2642aec53f4b..6fe228cf2d597 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -450,3 +450,5 @@ end export nnz scale!{T<:Base.LinAlg.BlasReal}(X::Array{T}, s::Complex) = error("scale!: Cannot scale a real array by a complex value in-place. Use scale(X::Array{Real}, s::Complex) instead.") + +@deprecate which(f::Callable, args...) @which f(args...) diff --git a/base/exports.jl b/base/exports.jl index ab32dc26f1c5e..aaf51cf2881c3 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1279,6 +1279,12 @@ export @allocated, @profile, @which, + @edit, + @less, + @code_typed, + @code_lowered, + @code_llvm, + @code_native, @windows, @unix, @osx, diff --git a/base/inference.jl b/base/inference.jl index 690266a5a782a..b0be0b902a246 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -2697,7 +2697,7 @@ function replace_tupleref!(ast, e::ANY, tupname, vals, sv, i0) end end -function code_typed(f::Callable, types) +function code_typed(f::Callable, types::(Type...)) asts = {} for x in _methods(f,types,-1) linfo = x[3].func.code diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index e18b0ec2bc62e..651209982e5a0 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -59,11 +59,10 @@ function less(file::String, line::Integer) run(`$pager +$(line)g $file`) end less(file::String) = less(file, 1) - -edit(f::Union(Function,DataType)) = edit(functionloc(f)...) -edit(f::Union(Function,DataType), t) = edit(functionloc(f,t)...) -less(f::Union(Function,DataType)) = less(functionloc(f)...) -less(f::Union(Function,DataType), t) = less(functionloc(f,t)...) +edit(f::Callable) = edit(functionloc(f)...) +edit(f::Callable, t::(Type...)) = edit(functionloc(f,t)...) +less(f::Callable) = less(functionloc(f)...) +less(f::Callable, t::(Type...)) = less(functionloc(f,t)...) function edit( m::Method ) tv, decls, file, line = arg_decl_parts(m) @@ -195,53 +194,68 @@ versioninfo(verbose::Bool) = versioninfo(STDOUT,verbose) # searching definitions -function which(f::Callable, args...) +function which(f::Callable, t::(Type...)) if !isgeneric(f) throw(ErrorException("not a generic function, no methods available")) end - ms = methods(f, map(a->(isa(a,Type) ? Type{a} : typeof(a)), args)) - isempty(ms) && throw(MethodError(f, args)) + ms = methods(f, t) + isempty(ms) && throw(MethodError(f, t)) ms[1] end -macro which(ex0) - if isa(ex0,Expr) && - any(a->(Meta.isexpr(a,:kw) || Meta.isexpr(a,:parameters)), ex0.args) +typesof(args...) = map(a->(isa(a,Type) ? Type{a} : typeof(a)), args) + +function gen_call_with_extracted_types(fcn, ex0) + if isa(ex0, Expr) && + any(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args) # keyword args not used in dispatch, so just remove them - args = filter(a->!(Meta.isexpr(a,:kw) || Meta.isexpr(a,:parameters)), ex0.args) - return Expr(:call, :which, map(esc, args)...) + args = filter(a->!(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args) + return Expr(:call, fcn, esc(args[1]), + Expr(:call, :typesof, map(esc, args[2:end])...)) end if isa(ex0, Expr) && ex0.head == :call - return Expr(:call, :which, map(esc, ex0.args)...) + return Expr(:call, fcn, esc(ex0.args[1]), + Expr(:call, :typesof, map(esc, ex0.args[2:end])...)) end ex = expand(ex0) exret = Expr(:call, :error, "expression is not a function call") if !isa(ex, Expr) # do nothing -> error elseif ex.head == :call - if any(e->(isa(e,Expr) && e.head==:(...)), ex0.args) && - isa(ex.args[1],TopNode) && ex.args[1].name == :apply - exret = Expr(:call, ex.args[1], :which, + if any(e->(isa(e, Expr) && e.head==:(...)), ex0.args) && + isa(ex.args[1], TopNode) && ex.args[1].name == :apply + exret = Expr(:call, ex.args[1], fcn, Expr(:tuple, esc(ex.args[2])), - map(esc, ex.args[3:end])...) + Expr(:call, :typesof, map(esc, ex.args[3:end])...)) else - exret = Expr(:call, :which, map(esc, ex.args)...) + 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, :which, a11, map(esc, a1.args[2:end])...) + exret = Expr(:call, fcn, a11, + Expr(:call, :typesof, map(esc, a1.args[2:end])...)) end end elseif ex.head == :thunk - exret = Expr(:call, :error, "expression is not a function call, or is too complex for @which to analyze; " + exret = Expr(:call, :error, "expression is not a function call, " + * "or is too complex for @which to analyze; " * "break it down to simpler parts if possible") end exret end +for fname in [:which, :less, :edit, :code_typed, :code_lowered, :code_llvm, :code_native] + @eval begin + macro ($fname)(ex0) + gen_call_with_extracted_types($fname, ex0) + end + end +end + # `methodswith` -- shows a list of methods using the type given function methodswith(t::Type, m::Module, showparents::Bool=false) diff --git a/base/reflection.jl b/base/reflection.jl index 326b9a57bb3d6..0ef447828b9b2 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -72,7 +72,7 @@ isgeneric(f::ANY) = (isa(f,Function)||isa(f,DataType)) && isa(f.env,MethodTable) function_name(f::Function) = isgeneric(f) ? f.env.name : (:anonymous) -code_lowered(f::Function,t::Tuple) = map(m->uncompressed_ast(m.func.code), methods(f,t)) +code_lowered(f::Function,t::(Type...)) = map(m->uncompressed_ast(m.func.code), methods(f,t)) methods(f::ANY,t::ANY) = map(m->m[3], _methods(f,t,-1))::Array{Any,1} _methods(f::ANY,t::ANY,lim) = _methods(f,{(t::Tuple)...},length(t::Tuple),lim,{}) function _methods(f::ANY,t::Array,i,lim::Integer,matching::Array{Any,1}) @@ -136,10 +136,10 @@ function _dump_function(f, t::ANY, native, wrapper) str end -code_llvm (f::Callable, types::Tuple) = print(_dump_function(f, types, false, false)) -code_native(f::Callable, types::Tuple) = print(_dump_function(f, types, true, false)) +code_llvm (f::Callable, types::(Type...)) = print(_dump_function(f, types, false, false)) +code_native(f::Callable, types::(Type...)) = print(_dump_function(f, types, true, false)) -function functionlocs(f::Union(Function,DataType), types=(Any...)) +function functionlocs(f::Callable, types=(Type...)) locs = Any[] for m in methods(f, types) lsd = m.func.code::LambdaStaticData @@ -154,7 +154,7 @@ function functionlocs(f::Union(Function,DataType), types=(Any...)) locs end -functionloc(f::Union(Function,DataType), types=(Any...)) = functionlocs(f, types)[1] +functionloc(f::Callable, types=(Any...)) = functionlocs(f, types)[1] function function_module(f::Function, types=(Any...)) m = methods(f, types) diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index ab612aaad6be9..e0a5cdb5399b7 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -49,6 +49,10 @@ Getting Around Edit the definition of a function, optionally specifying a tuple of types to indicate which method to edit. +.. function:: @edit + + Evaluates the arguments to the function call, determines their types, and calls the ``edit`` function on the resulting expression + .. function:: less(file::String, [line]) Show a file using the default pager, optionally providing a starting line number. Returns to the julia prompt when you quit the pager. @@ -57,6 +61,10 @@ Getting Around Show the definition of a function using the default pager, optionally specifying a tuple of types to indicate which method to see. +.. function:: @less + + Evaluates the arguments to the function call, determines their types, and calls the ``less`` function on the resulting expression + .. function:: clipboard(x) Send a printed form of ``x`` to the operating system clipboard ("copy"). @@ -89,9 +97,9 @@ Getting Around Search documentation for functions related to ``string``. -.. function:: which(f, args...) +.. function:: which(f, types) - Return the method of ``f`` (a ``Method`` object) that will be called for the given arguments. + Return the method of ``f`` (a ``Method`` object) that will be called for arguments with the given types. .. function:: @which @@ -5520,18 +5528,34 @@ Internals Returns an array of lowered ASTs for the methods matching the given generic function and type signature. +.. function:: @code_lowered + + Evaluates the arguments to the function call, determines their types, and calls the ``code_lowered`` function on the resulting expression + .. function:: code_typed(f, types) Returns an array of lowered and type-inferred ASTs for the methods matching the given generic function and type signature. +.. function:: @code_typed + + Evaluates the arguments to the function call, determines their types, and calls the ``code_typed`` function on the resulting expression + .. function:: code_llvm(f, types) Prints the LLVM bitcodes generated for running the method matching the given generic function and type signature to STDOUT. +.. function:: @code_llvm + + Evaluates the arguments to the function call, determines their types, and calls the ``code_llvm`` function on the resulting expression + .. function:: code_native(f, types) Prints the native assembly instructions generated for running the method matching the given generic function and type signature to STDOUT. +.. function:: @code_native + + Evaluates the arguments to the function call, determines their types, and calls the ``code_native`` function on the resulting expression + .. function:: precompile(f,args::(Any...,)) Compile the given function `f` for the argument tuple (of types) `args`, but do not execute it.