Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into codedb-ffi-io
Browse files Browse the repository at this point in the history
  • Loading branch information
brixen committed Feb 9, 2016
2 parents 503fad4 + 6e9bf80 commit c5db267
Show file tree
Hide file tree
Showing 22 changed files with 467 additions and 76 deletions.
23 changes: 23 additions & 0 deletions core/argf.rb
Expand Up @@ -363,6 +363,29 @@ def read(bytes=nil, output=nil)
output
end

def read_nonblock(maxlen, output = nil, exception: true)
output ||= default_value

unless advance!
output.clear
raise EOFError, "ARGF at end"
end

begin
out = @stream.read_nonblock(maxlen, output, exception: exception)

return out if out == :wait_readable
rescue EOFError => e
raise e if @use_stdin_only

@stream.close
@advance = true
advance! or raise e
end

return output
end

#
# Read next line of text.
#
Expand Down
55 changes: 55 additions & 0 deletions core/binding.rb
Expand Up @@ -58,4 +58,59 @@ def eval(expr, filename=nil, lineno=nil)
def local_variables
variables.local_variables
end

def local_variable_set(name, value)
unless name.is_a?(Symbol)
name = Rubinius::Type.coerce_to(name, String, :to_str).to_sym
end

vars = variables

# If a local variable is defined in a parent scope we should update the
# variable in said scope and all child scopes, instead of _only_ setting it
# in the current scope.
while vars
meth = vars.method

if meth.local_names.include?(name)
return vars.set_local(meth.local_slot(name), value)
elsif vars.eval_local_defined?(name)
return vars.set_eval_local(name, value)
end

vars = vars.parent
end

variables.set_eval_local(name, value)
end

def local_variable_get(name)
unless name.is_a?(Symbol)
name = Rubinius::Type.coerce_to(name, String, :to_str).to_sym
end

vars = variables

while vars
meth = vars.method

if meth.local_names.include?(name)
return vars.locals[meth.local_slot(name)]
elsif vars.eval_local_defined?(name)
return vars.get_eval_local(name)
end

vars = vars.parent
end

raise NameErrror, "local variable #{name.inspect} not defined for #{inspect}"
end

def local_variable_defined?(name)
unless name.is_a?(Symbol)
name = Rubinius::Type.coerce_to(name, String, :to_str).to_sym
end

variables.local_defined?(name)
end
end
4 changes: 4 additions & 0 deletions core/block_environment.rb
Expand Up @@ -102,6 +102,10 @@ def file
def defined_line
@block_env.line
end

def scope
@block_env.scope
end
end

def from_proc?
Expand Down
4 changes: 4 additions & 0 deletions core/delegated_method.rb
Expand Up @@ -31,5 +31,9 @@ def parameters
def source_location
@receiver.source_location
end

def scope
nil
end
end
end
35 changes: 35 additions & 0 deletions core/method.rb
Expand Up @@ -172,6 +172,23 @@ def super_method
return nil
end

def for_define_method(name, klass, callable_proc = nil)
Rubinius::Type.bindable_method? self.defined_in, klass

scope = @executable.scope

if @executable.is_a? Rubinius::DelegatedMethod
code = @executable
else
if callable_proc
code = Rubinius::DelegatedMethod.new(name, :call, callable_proc, false)
else
code = Rubinius::DelegatedMethod.new(name, :call_on_instance, self.unbind, true)
end
end

[code, scope]
end
end

##
Expand Down Expand Up @@ -316,4 +333,22 @@ def super_method

return nil
end

def for_define_method(name, klass, callable_proc = nil)
Rubinius::Type.bindable_method? self.defined_in, klass

scope = @executable.scope

if @executable.is_a? Rubinius::DelegatedMethod
code = @executable
else
if callable_proc
code = Rubinius::DelegatedMethod.new(name, :call, callable_proc, false)
else
code = Rubinius::DelegatedMethod.new(name, :call_on_instance, self, true)
end
end

[code, scope]
end
end
50 changes: 2 additions & 48 deletions core/module.rb
Expand Up @@ -442,54 +442,8 @@ def define_method(name, meth = undefined, &prc)

name = Rubinius::Type.coerce_to_symbol name

case meth
when Proc
if meth.ruby_method
code = Rubinius::DelegatedMethod.new(name, :call, meth, false)
scope = meth.ruby_method.executable.scope
else
be = meth.block.dup
be.change_name name
code = Rubinius::BlockEnvironment::AsMethod.new(be)
meth = meth.dup
meth.lambda_style!
scope = meth.block.scope
end
when Method
Rubinius::Type.bindable_method? meth.defined_in, self.class

exec = meth.executable
# We see through delegated methods because code creates these crazy calls
# to define_method over and over again and if we don't check, we create
# a huge delegated method chain. So instead, just see through them at one
# level always.
if exec.kind_of? Rubinius::DelegatedMethod
code = exec
scope = nil
else
code = Rubinius::DelegatedMethod.new(name, :call_on_instance, meth.unbind, true)
if exec.kind_of? Rubinius::CompiledCode
scope = exec.scope
else
scope = nil
end
end
when UnboundMethod
Rubinius::Type.bindable_method? meth.defined_in, self.class

exec = meth.executable
# Same reasoning as above.
if exec.kind_of? Rubinius::DelegatedMethod
code = exec
scope = nil
else
code = Rubinius::DelegatedMethod.new(name, :call_on_instance, meth, true)
if exec.kind_of? Rubinius::CompiledCode
scope = exec.scope
else
scope = nil
end
end
if meth.respond_to?(:for_define_method)
code, scope = meth.for_define_method(name, self.class)
else
raise TypeError, "wrong argument type #{meth.class} (expected Proc/Method)"
end
Expand Down
4 changes: 4 additions & 0 deletions core/native_method.rb
Expand Up @@ -78,5 +78,9 @@ def defined_line
def active_path
@file
end

def scope
nil
end
end
end
17 changes: 17 additions & 0 deletions core/proc.rb
Expand Up @@ -244,6 +244,23 @@ def dup
copy
end

def for_define_method(name, klass)
if @ruby_method
code, scope = @ruby_method.for_define_method(name, klass, self)
else
be = @block.dup
be.change_name name

duped_proc = self.dup
duped_proc.lambda_style!

code = Rubinius::BlockEnvironment::AsMethod.new(be)
scope = duped_proc.block.scope
end

[code, scope]
end

def self.from_method(meth)
if meth.kind_of? Method
return __from_method__(meth)
Expand Down
2 changes: 2 additions & 0 deletions core/variable_scope.rb
Expand Up @@ -136,6 +136,8 @@ def local_defined?(name)
return true if vars.eval_local_defined?(name, false)
vars = vars.parent
end

false
end

def local_layout
Expand Down
74 changes: 74 additions & 0 deletions spec/ruby/core/argf/read_nonblock_spec.rb
@@ -0,0 +1,74 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/read', __FILE__)

describe 'ARGF.read_nonblock' do
it_behaves_like :argf_read, :read_nonblock

before do
@file1_name = fixture(__FILE__, 'file1.txt')
@file2_name = fixture(__FILE__, 'file2.txt')

@file1 = File.read(@file1_name)
@file2 = File.read(@file2_name)

@chunk1 = File.read(@file1_name, 4)
@chunk2 = File.read(@file2_name, 4)
end

after do
ARGF.close unless ARGF.closed?
end

it 'reads up to the given amount of bytes' do
argv [@file1_name] do
ARGF.read_nonblock(4).should == @chunk1
end
end

describe 'when using multiple files' do
it 'reads up to the given amount of bytes from the first file' do
argv [@file1_name, @file2_name] do
ARGF.read_nonblock(4).should == @chunk1
end
end

it 'returns an empty String when reading after having read the first file in its entirety' do
argv [@file1_name, @file2_name] do
ARGF.read_nonblock(File.size(@file1_name)).should == @file1
ARGF.read_nonblock(4).should == ''
end
end
end

describe 'when ARGV is empty' do
it 'raises EOFError' do
proc { argv [] { ARGF.read_nonblock(4) } }.should raise_error(EOFError)
end
end

it 'reads up to the given bytes from STDIN' do
stdin = ruby_exe('print ARGF.read_nonblock(4)', :args => "< #{@file1_name}")

stdin.should == @chunk1
end

it 'reads up to the given bytes from a file when a file and STDIN are present' do
stdin = ruby_exe("print ARGF.read_nonblock(4)", :args => "#{@file1_name} - < #{@file2_name}")

stdin.should == @chunk1
end

it 'raises IO::EAGAINWaitReadable when STDIN is empty' do
input = 'ARGF.read_nonblock(4) rescue print $!.class'
stdin = ruby_exe(input, escape: true)

stdin.should == 'IO::EAGAINWaitReadable'
end

it 'returns :wait_readable when the :exception is set to false' do
input = 'p ARGF.read_nonblock(4, nil, exception: false)'
stdin = ruby_exe(input, escape: true)

stdin.strip.should == ':wait_readable'
end
end
25 changes: 1 addition & 24 deletions spec/ruby/core/argf/read_spec.rb
Expand Up @@ -5,7 +5,7 @@
it_behaves_like :argf_read, :read

before :each do

@file1_name = fixture __FILE__, "file1.txt"
@file2_name = fixture __FILE__, "file2.txt"
@stdin_name = fixture __FILE__, "stdin.txt"
Expand Down Expand Up @@ -66,27 +66,4 @@
ARGF.read.should == @file1 + @file1
end
end

with_feature :encoding do

before :each do
@external = Encoding.default_external
@internal = Encoding.default_internal

Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = nil
end

after :each do
Encoding.default_external = @external
Encoding.default_internal = @internal
end

it "reads the contents of the file with default encoding" do
Encoding.default_external = Encoding::US_ASCII
argv [@file1_name, @file2_name] do
ARGF.read.encoding.should == Encoding::US_ASCII
end
end
end
end
23 changes: 23 additions & 0 deletions spec/ruby/core/argf/shared/read.rb
Expand Up @@ -59,4 +59,27 @@
end
end
end

with_feature :encoding do
before :each do
@external = Encoding.default_external
@internal = Encoding.default_internal

Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = nil
end

after :each do
Encoding.default_external = @external
Encoding.default_internal = @internal
end

it "reads the contents of the file with default encoding" do
Encoding.default_external = Encoding::ASCII_8BIT

argv [@file1_name, @file2_name] do
ARGF.send(@method, 4).encoding.should == Encoding::ASCII_8BIT
end
end
end
end

0 comments on commit c5db267

Please sign in to comment.