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
brixen committed Feb 9, 2016
2 parents 503fad4 + 6e9bf80 commit c5db267
Showing 22 changed files with 467 additions and 76 deletions.
23 changes: 23 additions & 0 deletions core/argf.rb
Original file line number Diff line number Diff line change
@@ -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.
#
55 changes: 55 additions & 0 deletions core/binding.rb
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -102,6 +102,10 @@ def file
def defined_line
@block_env.line
end

def scope
@block_env.scope
end
end

def from_proc?
4 changes: 4 additions & 0 deletions core/delegated_method.rb
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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

##
@@ -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
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions core/native_method.rb
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 2 additions & 0 deletions core/variable_scope.rb
Original file line number Diff line number Diff line change
@@ -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
74 changes: 74 additions & 0 deletions spec/ruby/core/argf/read_nonblock_spec.rb
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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"
@@ -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
Original file line number Diff line number Diff line change
@@ -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
50 changes: 50 additions & 0 deletions spec/ruby/core/binding/local_variable_defined_p_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require File.expand_path('../../../spec_helper', __FILE__)

describe 'Binding#local_variable_defined?' do
it 'returns false when a variable is not defined' do
binding.local_variable_defined?(:foo).should == false
end

it 'returns true when a regular local variable is defined' do
foo = 10

binding.local_variable_defined?(:foo).should == true
end

it 'returns true when a local variable is defined using eval()' do
bind = binding

bind.eval('foo = 10')

bind.local_variable_defined?(:foo).should == true
end

it 'returns true when a local variable is defined using Binding#local_variable_set' do
bind = binding

bind.local_variable_set(:foo, 10)

bind.local_variable_defined?(:foo).should == true
end

it 'returns true when a local variable is defined in a parent scope' do
foo = 10

proc { binding.local_variable_defined?(:foo) }.call.should == true
end

it 'allows usage of a String as the variable name' do
foo = 10

binding.local_variable_defined?('foo').should == true
end

it 'allows usage of an object responding to #to_str as the variable name' do
foo = 10
name = mock(:obj)

name.stub!(:to_str).and_return('foo')

binding.local_variable_defined?(name).should == true
end
end
34 changes: 34 additions & 0 deletions spec/ruby/core/binding/local_variable_get_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require File.expand_path('../../../spec_helper', __FILE__)

describe 'Binding#local_variable_get' do
it 'gets a local variable defined before the Binding' do
number = 10

binding.local_variable_get(:number).should == 10
end

it 'gets a local variable defined in the Binding' do
bind = binding

bind.local_variable_set(:number, 10)
bind.local_variable_get(:number).should == 10
end

it 'gets a local variable defined using eval()' do
bind = binding

bind.eval('number = 10')

bind.local_variable_get(:number).should == 10
end

it 'gets a local variable defined in a parent scope' do
number = 10

proc { binding.local_variable_get(:number) }.call.should == 10
end

it 'raises NameError for an undefined local variable' do
proc { binding.local_variable_get(:cats) }.should raise_error(NameError)
end
end
54 changes: 54 additions & 0 deletions spec/ruby/core/binding/local_variable_set_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
require File.expand_path('../../../spec_helper', __FILE__)

describe 'Binding#local_variable_set' do
it 'sets a new local variable' do
bind = binding

bind.local_variable_set(:number, 10)
bind.local_variable_get(:number).should == 10
end

it 'sets a local variable using a String as the variable name' do
bind = binding

bind.local_variable_set('number', 10)
bind.local_variable_get('number').should == 10
end

it 'sets a local variable using an object responding to #to_str as the variable name' do
bind = binding
name = mock(:obj)

name.stub!(:to_str).and_return('number')

bind.local_variable_set(name, 10)
bind.local_variable_get(name).should == 10
end

it 'scopes new local variables to the receiving Binding' do
bind = binding

bind.local_variable_set(:number, 10)

proc { number }.should raise_error(NameError)
end

it 'overwrites an existing local variable defined before a Binding' do
number = 10
bind = binding

bind.local_variable_set(:number, 20)

number.should == 20
end

it 'overwrites a local variable defined using eval()' do
bind = binding

bind.eval('number = 10')

bind.local_variable_set(:number, 20)

bind.local_variable_get(:number).should == 20
end
end
20 changes: 20 additions & 0 deletions spec/ruby/core/module/define_method_spec.rb
Original file line number Diff line number Diff line change
@@ -180,6 +180,26 @@ def inspect_data
lambda{o.other_inspect}.should raise_error(NoMethodError)
end

it "accepts a proc from a method" do
class ProcFromMethod
attr_accessor :data
def cool_method
"data is #{@data}"
end
end

object1 = ProcFromMethod.new
object1.data = :foo

method_proc = object1.method(:cool_method).to_proc
klass = Class.new(ProcFromMethod)
klass.send(:define_method, :other_cool_method, &method_proc)

object2 = klass.new
object2.data = :bar
object2.other_cool_method.should == "data is foo"
end

it "maintains the Proc's scope" do
class DefineMethodByProcClass
in_scope = true
38 changes: 38 additions & 0 deletions spec/ruby/optional/capi/class_spec.rb
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
load_extension("class")

autoload :ClassUnderAutoload, "#{extension_path}/class_under_autoload_spec"
autoload :ClassIdUnderAutoload, "#{extension_path}/class_id_under_autoload_spec"

describe :rb_path_to_class, :shared => true do
it "returns a class or module from a scoped String" do
@@ -214,6 +215,43 @@

ClassUnderAutoload.name.should == "ClassUnderAutoload"
end

it "raises a TypeError if class is defined and its superclass mismatches the given one" do
lambda { @s.rb_define_class_under(CApiClassSpecs, "Sub", nil) }.should raise_error(TypeError)
end
end

describe "rb_define_class_id_under" do
it "creates a subclass of the superclass contained in a module" do
cls = @s.rb_define_class_id_under(CApiClassSpecs, :ClassIdUnder1, CApiClassSpecs::Super)
cls.should be_kind_of(Class)
CApiClassSpecs::Super.should be_ancestor_of(CApiClassSpecs::ClassIdUnder1)
end

it "uses Object as the superclass if NULL is passed" do
@s.rb_define_class_id_under(CApiClassSpecs, :ClassIdUnder2, nil)
Object.should be_ancestor_of(CApiClassSpecs::ClassIdUnder2)
end

it "sets the class name" do
cls = @s.rb_define_class_id_under(CApiClassSpecs, :ClassIdUnder3, nil)
cls.name.should == "CApiClassSpecs::ClassIdUnder3"
end

it "calls #inherited on the superclass" do
CApiClassSpecs::Super.should_receive(:inherited)
@s.rb_define_class_id_under(CApiClassSpecs, :ClassIdUnder4, CApiClassSpecs::Super)
end

it "defines a class for an existing Autoload" do
compile_extension("class_id_under_autoload")

ClassIdUnderAutoload.name.should == "ClassIdUnderAutoload"
end

it "raises a TypeError if class is defined and its superclass mismatches the given one" do
lambda { @s.rb_define_class_id_under(CApiClassSpecs, :Sub, nil) }.should raise_error(TypeError)
end
end

describe "rb_define_class_variable" do
5 changes: 5 additions & 0 deletions spec/ruby/optional/capi/ext/class_id_under_autoload_spec.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "ruby.h"

void Init_class_id_under_autoload_spec(void) {
rb_define_class_id_under(rb_cObject, rb_intern("ClassIdUnderAutoload"), 0);
}
12 changes: 12 additions & 0 deletions spec/ruby/optional/capi/ext/class_spec.c
Original file line number Diff line number Diff line change
@@ -160,6 +160,14 @@ static VALUE class_spec_rb_define_class_under(VALUE self, VALUE outer,
}
#endif

#ifdef HAVE_RB_DEFINE_CLASS_ID_UNDER
static VALUE class_spec_rb_define_class_id_under(VALUE self, VALUE outer,
VALUE name, VALUE super) {
if(NIL_P(super)) super = 0;
return rb_define_class_id_under(outer, SYM2ID(name), super);
}
#endif

#ifdef HAVE_RB_DEFINE_CLASS_VARIABLE
static VALUE class_spec_define_class_variable(VALUE self, VALUE klass, VALUE name, VALUE val) {
rb_define_class_variable(klass, StringValuePtr(name), val);
@@ -250,6 +258,10 @@ void Init_class_spec() {
rb_define_method(cls, "rb_define_class_under", class_spec_rb_define_class_under, 3);
#endif

#ifdef HAVE_RB_DEFINE_CLASS_ID_UNDER
rb_define_method(cls, "rb_define_class_id_under", class_spec_rb_define_class_id_under, 3);
#endif

#ifdef HAVE_RB_DEFINE_CLASS_VARIABLE
rb_define_method(cls, "rb_define_class_variable", class_spec_define_class_variable, 3);
#endif
1 change: 1 addition & 0 deletions spec/ruby/optional/capi/ext/rubyspec.h
Original file line number Diff line number Diff line change
@@ -423,6 +423,7 @@
#define HAVE_RB_CONST_SET 1
#define HAVE_RB_DEFINE_ALIAS 1
#define HAVE_RB_DEFINE_CLASS_UNDER 1
#define HAVE_RB_DEFINE_CLASS_ID_UNDER 1
#define HAVE_RB_DEFINE_CONST 1
#define HAVE_RB_DEFINE_GLOBAL_CONST 1
#define HAVE_RB_DEFINE_GLOBAL_FUNCTION 1
10 changes: 6 additions & 4 deletions vm/capi/class.cpp
Original file line number Diff line number Diff line change
@@ -172,14 +172,12 @@ extern "C" {
}

/** @note Shares code with rb_define_module_under, change there too. --rue */
VALUE rb_define_class_under(VALUE outer, const char* name, VALUE super) {


VALUE rb_define_class_id_under(VALUE outer, ID name, VALUE super) {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();

Module* module = c_as<Module>(env->get_object(outer));
Class* superclass = c_as<Class>(env->get_object(super ? super : rb_cObject));
Symbol* constant = env->state()->symbol(name);
Symbol* constant = reinterpret_cast<Symbol*>(name);

bool created = false;

@@ -212,6 +210,10 @@ extern "C" {
return klass;
}

VALUE rb_define_class_under(VALUE outer, const char* name, VALUE super) {
return rb_define_class_id_under(outer, rb_intern(name), super);
}

void rb_attr(VALUE klass, ID id, int read, int write, int ex) {
// TODO don't ignore ex.
if(read) {
3 changes: 3 additions & 0 deletions vm/include/capi/ruby/ruby.h
Original file line number Diff line number Diff line change
@@ -1300,6 +1300,9 @@ struct RTypedData {
/** Reopen or create new class with superclass and name under parent module. Returns the Class object. */
VALUE rb_define_class_under(VALUE parent, const char* name, VALUE superclass);

/** Reopen or create new class with superclass and name under parent module. Returns the Class object. */
VALUE rb_define_class_id_under(VALUE parent, ID name, VALUE superclass);

/** Define a toplevel constant */
void rb_define_global_const(const char* name, VALUE obj);

0 comments on commit c5db267

Please sign in to comment.