Skip to content

Commit

Permalink
Merge pull request #2052 from ruby/instance-eval
Browse files Browse the repository at this point in the history
Better `instance_eval`/`instance_exec` detection
  • Loading branch information
soutaro authored Oct 7, 2024
2 parents a97889a + 9695bb7 commit 16c0232
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 40 deletions.
85 changes: 45 additions & 40 deletions lib/rbs/test/hook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,50 +96,55 @@ def #{with_name}(#{param_source.join(", ")}, &#{block_param})
block_calls = []
if block_given?
receiver = self
block_receives_block = #{block_param}.parameters.last&.yield_self {|type, _| type == :block }
wrapped_block = proc do |*block_args, &block2|
return_from_block = false
begin
block_result = if receiver.equal?(self)
if block_receives_block
#{block_param}.call(*block_args, &block2)
else
yield(*block_args)
end
else
instance_exec(*block_args, &#{block_param})
end
return_from_block = true
ensure
exn = $!
case
when return_from_block
# Returned from yield
block_calls << ::RBS::Test::ArgumentsReturn.return(
arguments: block_args,
value: block_result
)
when exn
# Exception
block_calls << ::RBS::Test::ArgumentsReturn.exception(
arguments: block_args,
exception: exn
)
else
# break?
block_calls << ::RBS::Test::ArgumentsReturn.break(
arguments: block_args
)
wrapped_block = Object.new.instance_exec do
new_object = self
proc do |*block_args, &block2|
return_from_block = false
begin
block_result = if self.equal?(new_object)
if block_receives_block
#{block_param}.call(*block_args, &block2)
else
yield(*block_args)
end
else
# Detected that `self` inside `proc` is not equal to the `self` outside block (which is saved to `new_object`).
# This means that the block is called with `instance_exec` or `instance_eval`.
self.instance_exec(*block_args, &#{block_param})
end
return_from_block = true
ensure
exn = $!
case
when return_from_block
# Returned from yield
block_calls << ::RBS::Test::ArgumentsReturn.return(
arguments: block_args,
value: block_result
)
when exn
# Exception
block_calls << ::RBS::Test::ArgumentsReturn.exception(
arguments: block_args,
exception: exn
)
else
# break?
block_calls << ::RBS::Test::ArgumentsReturn.break(
arguments: block_args
)
end
end
end
block_result
end.ruby2_keywords
block_result
end.ruby2_keywords
end
result = __send__(:"#{without_name}", *args, &wrapped_block)
else
Expand Down
60 changes: 60 additions & 0 deletions test/rbs/test/runtime_test_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,66 @@ def test_String
ruby_content: <<RUBY
class String
end
RUBY
)
end

def test__untyped_block
assert_test_success(
other_env: { "RBS_TEST_TARGET" => "::Base,::DSL::ParameterBuilder" },
rbs_content: <<RBS, ruby_content: <<RUBY
class Base
def self.parameter: (Symbol) ?{ () [self: DSL::ParameterBuilder] -> void } -> void
def self.parameter_builder: () -> DSL::ParameterBuilder
end
module DSL
class ParameterBuilder
def define: (Symbol) ?{ () [self: self] -> void } -> self
def build!: () -> void
def description: (String) -> void
alias desc description
end
end
RBS
class Base
class << self
def parameter(parameter_name, &)
parameter_builder.define(parameter_name, &).build!
end
private
def parameter_builder
@parameter_builder ||= DSL::ParameterBuilder.new()
end
end
end
module DSL
class ParameterBuilder
def define(parameter_name, &)
instance_exec(&) if block_given?
self
end
def build!
end
def description(description_string)
self
end
alias desc description
end
end
Base.parameter(:foo) do
desc "Some message here"
end
RUBY
)
end
Expand Down

0 comments on commit 16c0232

Please sign in to comment.