1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
# frozen_string_literal: true
module Byebug
module Helpers
#
# Utilities to assist evaluation of code strings
#
module EvalHelper
#
# Evaluates an +expression+ in a separate thread.
#
# @param expression [String] Expression to evaluate
#
def separate_thread_eval(expression)
allowing_other_threads do
in_new_thread { warning_eval(expression) }
end
end
#
# Evaluates an +expression+ that might use or defer execution to threads
# other than the current one.
#
# @note This is necessary because when in byebug's prompt, every thread is
# "frozen" so that nothing gets run. So we need to unlock threads prior
# to evaluation or we will run into a deadlock.
#
# @param expression [String] Expression to evaluate
#
def multiple_thread_eval(expression)
allowing_other_threads { warning_eval(expression) }
end
#
# Evaluates a string containing Ruby code in a specific binding,
# returning nil in an error happens.
#
def silent_eval(str, binding = frame._binding)
safe_eval(str, binding) { |_e| nil }
end
#
# Evaluates a string containing Ruby code in a specific binding,
# handling the errors at an error level.
#
def error_eval(str, binding = frame._binding)
safe_eval(str, binding) { |e| raise(e, msg(e)) }
end
#
# Evaluates a string containing Ruby code in a specific binding,
# handling the errors at a warning level.
#
def warning_eval(str, binding = frame._binding)
safe_eval(str, binding) { |e| errmsg(msg(e)) }
end
private
def safe_eval(str, binding)
binding.eval(str.gsub(/\Aeval /, ""), "(byebug)", 1)
rescue StandardError, ScriptError => e
yield(e)
end
def msg(exception)
msg = Setting[:stack_on_error] ? error_msg(exception) : warning_msg(exception)
pr("eval.exception", text_message: msg)
end
def error_msg(exception)
at = exception.backtrace
locations = ["#{at.shift}: #{warning_msg(exception)}"]
locations += at.map { |path| " from #{path}" }
locations.join("\n")
end
def warning_msg(exception)
"#{exception.class} Exception: #{exception.message}"
end
#
# Run block temporarily ignoring all TracePoint events.
#
# Used to evaluate stuff within Byebug's prompt. Otherwise, any code
# creating new threads won't be properly evaluated because new threads
# will get blocked by byebug's main thread.
#
def allowing_other_threads
Byebug.unlock
res = yield
Byebug.lock
res
end
#
# Runs the given block in a new thread, waits for it to finish and
# returns the new thread's result.
#
def in_new_thread
res = nil
Thread.new { res = yield }.join
res
end
def safe_inspect(var)
var.inspect
rescue StandardError
safe_to_s(var)
end
def safe_to_s(var)
var.to_s
rescue StandardError
"*Error in evaluation*"
end
end
end
end
|