Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@443 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
- Loading branch information
Showing
1 changed file
with
72 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -1,81 +1,85 @@ | |||
begin | begin | ||
require 'simplecc' | require 'simplecc' | ||
rescue LoadError | rescue LoadError | ||
def Continuation.create(*args, &block) #:nodoc: | class Continuation #:nodoc: | ||
cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?} | def create(*args, &block) | ||
result ||= args | cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?} | ||
return *[cc, *result] | result ||= args | ||
return *[cc, *result] | |||
end | |||
end | end | ||
end | end | ||
|
|
||
# This method returns the binding of the method that called your | class Binding #:nodoc: | ||
# method. It will raise an Exception when you're not inside a method. | # This method returns the binding of the method that called your | ||
# | # method. It will raise an Exception when you're not inside a method. | ||
# It's used like this: | # | ||
# def inc_counter(amount = 1) | # It's used like this: | ||
# Binding.of_caller do |binding| | # def inc_counter(amount = 1) | ||
# # Create a lambda that will increase the variable 'counter' | # Binding.of_caller do |binding| | ||
# # in the caller of this method when called. | # # Create a lambda that will increase the variable 'counter' | ||
# inc = eval("lambda { |arg| counter += arg }", binding) | # # in the caller of this method when called. | ||
# # We can refer to amount from inside this block safely. | # inc = eval("lambda { |arg| counter += arg }", binding) | ||
# inc.call(amount) | # # We can refer to amount from inside this block safely. | ||
# end | # inc.call(amount) | ||
# # No other statements can go here. Put them inside the block. | # end | ||
# end | # # No other statements can go here. Put them inside the block. | ||
# counter = 0 | # end | ||
# 2.times { inc_counter } | # counter = 0 | ||
# counter # => 2 | # 2.times { inc_counter } | ||
# | # counter # => 2 | ||
# Binding.of_caller must be the last statement in the method. | # | ||
# This means that you will have to put everything you want to | # Binding.of_caller must be the last statement in the method. | ||
# do after the call to Binding.of_caller into the block of it. | # This means that you will have to put everything you want to | ||
# This should be no problem however, because Ruby has closures. | # do after the call to Binding.of_caller into the block of it. | ||
# If you don't do this an Exception will be raised. Because of | # This should be no problem however, because Ruby has closures. | ||
# the way that Binding.of_caller is implemented it has to be | # If you don't do this an Exception will be raised. Because of | ||
# done this way. | # the way that Binding.of_caller is implemented it has to be | ||
def Binding.of_caller(&block) #:nodoc: | # done this way. | ||
old_critical = Thread.critical | def of_caller(&block) | ||
Thread.critical = true | old_critical = Thread.critical | ||
count = 0 | Thread.critical = true | ||
cc, result, error, extra_data = Continuation.create(nil, nil) | count = 0 | ||
error.call if error | cc, result, error, extra_data = Continuation.create(nil, nil) | ||
error.call if error | |||
|
|
||
tracer = lambda do |*args| | tracer = lambda do |*args| | ||
type, context, extra_data = args[0], args[4], args | type, context, extra_data = args[0], args[4], args | ||
if type == "return" | if type == "return" | ||
count += 1 | count += 1 | ||
# First this method and then calling one will return -- | # First this method and then calling one will return -- | ||
# the trace event of the second event gets the context | # the trace event of the second event gets the context | ||
# of the method which called the method that called this | # of the method which called the method that called this | ||
# method. | # method. | ||
if count == 2 | if count == 2 | ||
# It would be nice if we could restore the trace_func | # It would be nice if we could restore the trace_func | ||
# that was set before we swapped in our own one, but | # that was set before we swapped in our own one, but | ||
# this is impossible without overloading set_trace_func | # this is impossible without overloading set_trace_func | ||
# in current Ruby. | # in current Ruby. | ||
set_trace_func(nil) | |||
cc.call(eval("binding", context), nil, extra_data) | |||
end | |||
elsif type == "line" then | |||
nil | |||
elsif type == "c-return" and extra_data[3] == :set_trace_func then | |||
nil | |||
else | |||
set_trace_func(nil) | set_trace_func(nil) | ||
cc.call(eval("binding", context), nil, extra_data) | error_msg = "Binding.of_caller used in non-method context or " + | ||
"trailing statements of method using it aren't in the block." | |||
cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil) | |||
end | end | ||
elsif type == "line" then | |||
nil | |||
elsif type == "c-return" and extra_data[3] == :set_trace_func then | |||
nil | |||
else | |||
set_trace_func(nil) | |||
error_msg = "Binding.of_caller used in non-method context or " + | |||
"trailing statements of method using it aren't in the block." | |||
cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil) | |||
end | end | ||
end | |||
|
|
||
unless result | unless result | ||
set_trace_func(tracer) | set_trace_func(tracer) | ||
return nil | return nil | ||
else | else | ||
Thread.critical = old_critical | Thread.critical = old_critical | ||
case block.arity | case block.arity | ||
when 1 then yield(result) | when 1 then yield(result) | ||
else yield(result, extra_data) | else yield(result, extra_data) | ||
end | |||
end | end | ||
end | end | ||
end | end |