summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--io_buffer.c146
-rw-r--r--lib/bundler/source/rubygems.rb2
-rw-r--r--lib/bundler/source_list.rb4
-rw-r--r--spec/bundler/install/cooldown_spec.rb282
-rw-r--r--test/-ext-/symbol/test_inadvertent_creation.rb43
-rw-r--r--test/ruby/test_io_buffer.rb41
6 files changed, 485 insertions, 33 deletions
diff --git a/io_buffer.c b/io_buffer.c
index d9f50fc234..dc8f547a32 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -899,8 +899,8 @@ rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
}
// Internal function for accessing bytes for writing, wil
-static inline void
-io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t *size)
+static void
+io_buffer_validate_for_writing(struct rb_io_buffer *buffer)
{
if (buffer->flags & RB_IO_BUFFER_READONLY ||
(!NIL_P(buffer->source) && OBJ_FROZEN(buffer->source))) {
@@ -910,6 +910,21 @@ io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t
if (!io_buffer_validate(buffer)) {
rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
}
+}
+
+static struct rb_io_buffer *
+get_io_buffer_for_writing(VALUE self)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+ io_buffer_validate_for_writing(buffer);
+ return buffer;
+}
+
+static inline void
+io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t *size)
+{
+ io_buffer_validate_for_writing(buffer);
if (buffer->base) {
*base = buffer->base;
@@ -930,11 +945,17 @@ rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size)
}
static void
-io_buffer_get_bytes_for_reading(struct rb_io_buffer *buffer, const void **base, size_t *size)
+io_buffer_validate_for_reading(struct rb_io_buffer *buffer)
{
if (!io_buffer_validate(buffer)) {
rb_raise(rb_eIOBufferInvalidatedError, "Buffer has been invalidated!");
}
+}
+
+static void
+io_buffer_get_bytes_for_reading(struct rb_io_buffer *buffer, const void **base, size_t *size)
+{
+ io_buffer_validate_for_reading(buffer);
if (buffer->base) {
*base = buffer->base;
@@ -1373,14 +1394,23 @@ io_buffer_readonly_p(VALUE self)
return RBOOL(rb_io_buffer_readonly_p(self));
}
-static void
-io_buffer_lock(struct rb_io_buffer *buffer)
+static int
+io_buffer_try_lock(struct rb_io_buffer *buffer)
{
if (buffer->flags & RB_IO_BUFFER_LOCKED) {
- rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
+ return 0;
}
buffer->flags |= RB_IO_BUFFER_LOCKED;
+ return 1;
+}
+
+static void
+io_buffer_lock(struct rb_io_buffer *buffer)
+{
+ if (!io_buffer_try_lock(buffer)) {
+ rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
+ }
}
VALUE
@@ -1548,6 +1578,8 @@ size_sum_is_bigger_than(size_t a, size_t b, size_t x)
static inline void
io_buffer_validate_range(struct rb_io_buffer *buffer, size_t offset, size_t length)
{
+ io_buffer_validate_for_reading(buffer);
+
if (size_sum_is_bigger_than(offset, length, buffer->size)) {
rb_raise(rb_eArgError, "Specified offset+length is bigger than the buffer size!");
}
@@ -1752,6 +1784,8 @@ rb_io_buffer_resize(VALUE self, size_t size)
struct rb_io_buffer *buffer = NULL;
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+ io_buffer_validate_for_reading(buffer);
+
if (buffer->flags & RB_IO_BUFFER_LOCKED) {
rb_raise(rb_eIOBufferLockedError, "Cannot resize locked buffer!");
}
@@ -1954,6 +1988,10 @@ ruby_swap128_int(rb_int128_t x)
return conversion.int128;
}
+#define IO_BUFFER_VALIDATE_TYPE_FOR_WRITING(buffer, base, size, offset, type) \
+ (io_buffer_get_bytes_for_writing(buffer, &(base), &(size)), \
+ io_buffer_validate_type(size, offset, sizeof(type)))
+
#define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
static ID RB_IO_BUFFER_DATA_TYPE_##name; \
\
@@ -1969,10 +2007,12 @@ io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
} \
\
static void \
-io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _value) \
+io_buffer_write_##name(struct rb_io_buffer* buffer, size_t *offset, VALUE _value) \
{ \
- io_buffer_validate_type(size, *offset, sizeof(type)); \
+ void* base; size_t size; \
+ IO_BUFFER_VALIDATE_TYPE_FOR_WRITING(buffer, base, size, *offset, type); \
type value = unwrap(_value); \
+ IO_BUFFER_VALIDATE_TYPE_FOR_WRITING(buffer, base, size, *offset, type); \
if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
memcpy((char*)base + *offset, &value, sizeof(type)); \
*offset += sizeof(type); \
@@ -2042,6 +2082,15 @@ io_buffer_buffer_type_size(ID buffer_type)
rb_raise(rb_eArgError, "Invalid type name!");
}
+static inline ID
+io_buffer_type_id(VALUE name)
+{
+ Check_Type(name, T_SYMBOL);
+ if (!STATIC_SYM_P(name)) return 0;
+ return rb_sym2id(name);
+}
+#define TYPE_ID(name) io_buffer_type_id(name)
+
/*
* call-seq:
* size_of(buffer_type) -> byte size
@@ -2058,12 +2107,12 @@ io_buffer_size_of(VALUE klass, VALUE buffer_type)
if (RB_TYPE_P(buffer_type, T_ARRAY)) {
size_t total = 0;
for (long i = 0; i < RARRAY_LEN(buffer_type); i++) {
- total += io_buffer_buffer_type_size(RB_SYM2ID(RARRAY_AREF(buffer_type, i)));
+ total += io_buffer_buffer_type_size(TYPE_ID(RARRAY_AREF(buffer_type, i)));
}
return SIZET2NUM(total);
}
else {
- return SIZET2NUM(io_buffer_buffer_type_size(RB_SYM2ID(buffer_type)));
+ return SIZET2NUM(io_buffer_buffer_type_size(TYPE_ID(buffer_type)));
}
}
@@ -2150,7 +2199,7 @@ io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
rb_io_buffer_get_bytes_for_reading(self, &base, &size);
- return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset);
+ return rb_io_buffer_get_value(base, size, TYPE_ID(type), &offset);
}
/*
@@ -2180,7 +2229,7 @@ io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset)
for (long i = 0; i < RARRAY_LEN(buffer_types); i++) {
VALUE type = rb_ary_entry(buffer_types, i);
- VALUE value = rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset);
+ VALUE value = rb_io_buffer_get_value(base, size, TYPE_ID(type), &offset);
rb_ary_push(array, value);
}
@@ -2249,7 +2298,7 @@ io_buffer_each(int argc, VALUE *argv, VALUE self)
ID buffer_type;
if (argc >= 1) {
- buffer_type = RB_SYM2ID(argv[0]);
+ buffer_type = TYPE_ID(argv[0]);
}
else {
buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
@@ -2287,7 +2336,7 @@ io_buffer_values(int argc, VALUE *argv, VALUE self)
ID buffer_type;
if (argc >= 1) {
- buffer_type = RB_SYM2ID(argv[0]);
+ buffer_type = TYPE_ID(argv[0]);
}
else {
buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
@@ -2347,9 +2396,10 @@ io_buffer_each_byte(int argc, VALUE *argv, VALUE self)
}
static inline void
-rb_io_buffer_set_value(const void* base, size_t size, ID buffer_type, size_t *offset, VALUE value)
+rb_io_buffer_set_value(struct rb_io_buffer *buffer, VALUE buffer_type, size_t *offset, VALUE value)
{
-#define IO_BUFFER_SET_VALUE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(base, size, offset, value); return;}
+ ID type = TYPE_ID(buffer_type);
+#define IO_BUFFER_SET_VALUE(name) if (type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(buffer, offset, value); return;}
IO_BUFFER_SET_VALUE(U8);
IO_BUFFER_SET_VALUE(S8);
@@ -2382,6 +2432,21 @@ rb_io_buffer_set_value(const void* base, size_t size, ID buffer_type, size_t *of
rb_raise(rb_eArgError, "Invalid type name!");
}
+struct io_buffer_set_value_arguments {
+ struct rb_io_buffer *buffer;
+ size_t offset;
+ VALUE type, value;
+};
+
+static VALUE
+io_buffer_set_value_try(VALUE arguments)
+{
+ struct io_buffer_set_value_arguments *args = (void *)arguments;
+ size_t offset = args->offset;
+ rb_io_buffer_set_value(args->buffer, args->type, &offset, args->value);
+ return SIZET2NUM(offset);
+}
+
/*
* call-seq: set_value(type, offset, value) -> offset
*
@@ -2415,13 +2480,30 @@ rb_io_buffer_set_value(const void* base, size_t size, ID buffer_type, size_t *of
static VALUE
io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
{
- void *base;
- size_t size;
- size_t offset = io_buffer_extract_offset(_offset);
+ struct io_buffer_set_value_arguments arguments = {
+ .buffer = get_io_buffer_for_writing(self),
+ .offset = io_buffer_extract_offset(_offset),
+ .type = type,
+ .value = value,
+ };
- rb_io_buffer_get_bytes_for_writing(self, &base, &size);
+ if (!io_buffer_try_lock(arguments.buffer)) {
+ return io_buffer_set_value_try((VALUE)&arguments);
+ }
+ return rb_ensure(io_buffer_set_value_try, (VALUE)&arguments, rb_io_buffer_locked_ensure, self);
+}
- rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value);
+static VALUE
+io_buffer_set_values_try(VALUE arguments)
+{
+ struct io_buffer_set_value_arguments *args = (void *)arguments;
+ size_t offset = args->offset;
+
+ for (long i = 0; i < RARRAY_LEN(args->type); i++) {
+ VALUE type = rb_ary_entry(args->type, i);
+ VALUE value = rb_ary_entry(args->value, i);
+ rb_io_buffer_set_value(args->buffer, type, &offset, value);
+ }
return SIZET2NUM(offset);
}
@@ -2443,31 +2525,31 @@ io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
static VALUE
io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values)
{
+ struct io_buffer_set_value_arguments arguments = {
+ .buffer = get_io_buffer_for_writing(self),
+ };
+
if (!RB_TYPE_P(buffer_types, T_ARRAY)) {
rb_raise(rb_eArgError, "Argument buffer_types should be an array!");
}
+ arguments.type = buffer_types;
+
+ arguments.offset = io_buffer_extract_offset(_offset);
if (!RB_TYPE_P(values, T_ARRAY)) {
rb_raise(rb_eArgError, "Argument values should be an array!");
}
+ arguments.value = values;
if (RARRAY_LEN(buffer_types) != RARRAY_LEN(values)) {
rb_raise(rb_eArgError, "Argument buffer_types and values should have the same length!");
}
- size_t offset = io_buffer_extract_offset(_offset);
-
- void *base;
- size_t size;
- rb_io_buffer_get_bytes_for_writing(self, &base, &size);
-
- for (long i = 0; i < RARRAY_LEN(buffer_types); i++) {
- VALUE type = rb_ary_entry(buffer_types, i);
- VALUE value = rb_ary_entry(values, i);
- rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value);
+ if (!io_buffer_try_lock(arguments.buffer)) {
+ return io_buffer_set_values_try((VALUE)&arguments);
}
+ return rb_ensure(io_buffer_set_values_try, (VALUE)&arguments, rb_io_buffer_locked_ensure, self);
- return SIZET2NUM(offset);
}
static size_t IO_BUFFER_BLOCKING_SIZE = 1024*1024;
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
index ed864604fe..9109f399a7 100644
--- a/lib/bundler/source/rubygems.rb
+++ b/lib/bundler/source/rubygems.rb
@@ -11,7 +11,7 @@ module Bundler
API_REQUEST_SIZE = 100
REQUIRE_MUTEX = Mutex.new
- attr_accessor :remotes
+ attr_accessor :remotes, :remote_cooldowns
def initialize(options = {})
@options = options
diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb
index ab7002d6e5..954efbb65f 100644
--- a/lib/bundler/source_list.rb
+++ b/lib/bundler/source_list.rb
@@ -169,6 +169,10 @@ module Bundler
# locked sources never include credentials so always prefer remotes from the gemfile
replacement_source.remotes = gemfile_source.remotes
+ # cooldowns are only ever declared in the Gemfile, so carry them over
+ # along with the remotes they apply to
+ replacement_source.remote_cooldowns = gemfile_source.remote_cooldowns
+
yield replacement_source if block_given?
replacement_source
diff --git a/spec/bundler/install/cooldown_spec.rb b/spec/bundler/install/cooldown_spec.rb
index bad7b7cf34..01e87be663 100644
--- a/spec/bundler/install/cooldown_spec.rb
+++ b/spec/bundler/install/cooldown_spec.rb
@@ -143,6 +143,64 @@ RSpec.describe "bundle install with the cooldown setting" do
expect(the_bundle).to include_gems("ripe_gem 1.0.0")
end
+ it "applies per-source Gemfile cooldown on bundle update when a lockfile exists" do
+ # Converging the Gemfile sources with the lockfile sources used to drop
+ # the per-source cooldown, so it only ever worked on a first resolve
+ # without a lockfile.
+ gemfile <<-G
+ source "https://gem.repo3", cooldown: 7
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update ripe_gem", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0")
+ end
+
+ it "applies per-source Gemfile cooldown to gems added after the lockfile was written" do
+ gemfile <<-G
+ source "https://gem.repo3", cooldown: 7
+ gem "ripe_gem"
+ gem "child"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0", "child 1.0.0")
+ end
+
it "is overridden by CLI --cooldown when Gemfile sets a different per-source value" do
gemfile <<-G
source "https://gem.repo3", cooldown: 0
@@ -429,5 +487,229 @@ RSpec.describe "bundle install with the cooldown setting" do
expect(the_bundle).to include_gems("upgradable 3.0.0")
end
+
+ it "keeps a top-level source cooldown through a partial update with multiple sources" do
+ now = Time.now.utc
+ build_repo4 do
+ build_gem "solo_gem", "1.0.0" do |s|
+ s.date = now - (30 * 86_400)
+ end
+ build_gem "solo_gem", "2.0.0" do |s|
+ s.date = now - (1 * 86_400)
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo3", cooldown: 7
+ gem "ripe_gem"
+ source "https://gem.repo4" do
+ gem "solo_gem"
+ end
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (1.0.0)
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ solo_gem (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+ solo_gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update ripe_gem", artifice: "compact_index_cooldown"
+
+ # A partial update converges the still-locked sources, the path that used
+ # to drop cooldown. repo3's cooldown must survive that even with a second
+ # source in the Gemfile, so its in-window 2.0.0 stays excluded.
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0", "solo_gem 1.0.0")
+ end
+
+ it "carries cooldown declared on a gem-block source" do
+ now = Time.now.utc
+ build_repo4 do
+ build_gem "solo_gem", "1.0.0" do |s|
+ s.date = now - (30 * 86_400)
+ end
+ build_gem "solo_gem", "2.0.0" do |s|
+ s.date = now - (1 * 86_400)
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ source "https://gem.repo4", cooldown: 7 do
+ gem "solo_gem"
+ end
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (1.0.0)
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ solo_gem (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+ solo_gem!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update solo_gem", artifice: "compact_index_cooldown"
+
+ # The cooldown lives on the gem-block source, which is also converged from
+ # the lockfile. A partial update of solo_gem must keep that cooldown, so
+ # its in-window 2.0.0 stays excluded.
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0", "solo_gem 1.0.0")
+ end
+
+ it "applies per-source Gemfile cooldown to a gem added via bundle add" do
+ gemfile <<-G
+ source "https://gem.repo3", cooldown: 7
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "add child", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("child 1.0.0")
+ end
+
+ it "applies per-source Gemfile cooldown on bundle lock --update" do
+ gemfile <<-G
+ source "https://gem.repo3", cooldown: 7
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update ripe_gem", artifice: "compact_index_cooldown"
+
+ expect(lockfile).to include("ripe_gem (1.0.0)")
+ expect(lockfile).not_to include("ripe_gem (2.0.0)")
+ end
+
+ it "ignores cooldown and installs the locked version when frozen" do
+ # Frozen installs read the lockfile instead of resolving, so cooldown has
+ # no say. A version already locked inside the window must still install.
+ gemfile <<-G
+ source "https://gem.repo3", cooldown: 7
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "config set frozen true"
+ bundle "install", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 2.0.0")
+ end
+
+ it "keys per-source cooldown by the declared URI even behind a mirror" do
+ # A mirror rewrites the fetch URI, but cooldown is recorded under the URI
+ # written in the Gemfile. The cooldown must still apply through the
+ # redirect to the mirror that actually serves the gems.
+ bundle "config set mirror.https://gem.repo2 https://gem.repo3"
+
+ gemfile <<-G
+ source "https://gem.repo2", cooldown: 7
+ gem "ripe_gem"
+ G
+
+ bundle "install", artifice: "compact_index_cooldown",
+ env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo3.to_s }
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0")
+ end
+ end
+
+ context "with a source that does not provide publish dates" do
+ before do
+ build_repo3 do
+ build_gem "ripe_gem", "1.0.0"
+ build_gem "ripe_gem", "2.0.0"
+ end
+ end
+
+ it "cannot apply cooldown and installs the latest version" do
+ # The legacy dependency API does not expose per-version publish dates, so
+ # the cooldown filter has nothing to compare against and is silently
+ # inactive. This pins that limitation; flip the expectation if publish
+ # dates ever become available over this endpoint.
+ gemfile <<-G
+ source "https://gem.repo3", cooldown: 7
+ gem "ripe_gem"
+ G
+
+ bundle "install", artifice: "endpoint"
+
+ expect(the_bundle).to include_gems("ripe_gem 2.0.0")
+ end
end
end
diff --git a/test/-ext-/symbol/test_inadvertent_creation.rb b/test/-ext-/symbol/test_inadvertent_creation.rb
index 995e01ee15..44705e10c7 100644
--- a/test/-ext-/symbol/test_inadvertent_creation.rb
+++ b/test/-ext-/symbol/test_inadvertent_creation.rb
@@ -489,5 +489,48 @@ module Test_Symbol
Bug::Symbol.iv_get(obj, name)
end
end
+
+ def assert_io_buffer_no_immortal_symbol_created(buffer = IO::Buffer.new(128))
+ assert_no_immortal_symbol_created("io_buffer") do |name|
+ yield buffer, name.to_sym
+ end
+ end
+
+ def test_io_buffer_size_of_inadvertent_id_creation
+ assert_io_buffer_no_immortal_symbol_created(nil) do |_, name|
+ assert_raise(ArgumentError) {IO::Buffer.size_of(name)}
+ assert_raise(ArgumentError) {IO::Buffer.size_of([name])}
+ end
+ end
+
+ def test_io_buffer_each_inadvertent_id_creation
+ assert_io_buffer_no_immortal_symbol_created do |buffer, name|
+ assert_raise(ArgumentError) {buffer.each(name, 0, 1) {}}
+ end
+ end
+
+ def test_io_buffer_get_value_inadvertent_id_creation
+ assert_io_buffer_no_immortal_symbol_created do |buffer, name|
+ assert_raise(ArgumentError) {buffer.get_value(name, 0)}
+ end
+ end
+
+ def test_io_buffer_get_values_inadvertent_id_creation
+ assert_io_buffer_no_immortal_symbol_created do |buffer, name|
+ assert_raise(ArgumentError) {buffer.get_values([name], 0)}
+ end
+ end
+
+ def test_io_buffer_set_value_inadvertent_id_creation
+ assert_io_buffer_no_immortal_symbol_created do |buffer, name|
+ assert_raise(ArgumentError) {buffer.set_value(name, 0, 0)}
+ end
+ end
+
+ def test_io_buffer_set_values_inadvertent_id_creation
+ assert_io_buffer_no_immortal_symbol_created do |buffer, name|
+ assert_raise(ArgumentError) {buffer.set_values([name], 0, [0])}
+ end
+ end
end
end
diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb
index 327a3ece9c..fdf99589ef 100644
--- a/test/ruby/test_io_buffer.rb
+++ b/test/ruby/test_io_buffer.rb
@@ -242,6 +242,16 @@ class TestIOBuffer < Test::Unit::TestCase
end
end
+ def test_resize_invalidated_slice
+ inner = IO::Buffer.new(IO::Buffer::PAGE_SIZE)
+ slice = inner.slice(0, 8)
+ inner.free
+
+ assert_raise(IO::Buffer::InvalidatedError) do
+ slice.resize(16)
+ end
+ end
+
def test_compare_same_size
buffer1 = IO::Buffer.new(1)
assert_equal buffer1, buffer1
@@ -376,6 +386,17 @@ class TestIOBuffer < Test::Unit::TestCase
assert_raise_with_message(ArgumentError, /Offset can't be negative/) do
buffer.get_string(-1)
end
+
+ encoding = Struct.new(:buffer) do
+ def to_str
+ buffer.free
+ "BINARY"
+ end
+ end.new(buffer.dup)
+ slice = encoding.buffer.slice(0, 8)
+ assert_raise(IO::Buffer::InvalidatedError) do
+ slice.get_string(0, 8, encoding)
+ end
end
def test_zero_length_get_string
@@ -449,6 +470,26 @@ class TestIOBuffer < Test::Unit::TestCase
end
end
+ def test_set_values_invalidated_slice
+ to_int = Struct.new(:buffer) do
+ def to_int
+ buffer.free
+ 0x41
+ end
+ end
+ buffer = IO::Buffer.new(128)
+ slice = buffer.slice(0, 8)
+ value = to_int.new(buffer)
+ assert_raise(IO::Buffer::InvalidatedError) {slice.set_value(:U8, 0, value)}
+
+ buffer = IO::Buffer.new(128)
+ slice = buffer.slice(0, 8)
+ value = to_int.new(buffer)
+ assert_raise(IO::Buffer::InvalidatedError) {
+ slice.set_values([:U8, :U8], 0, [0, value])
+ }
+ end
+
def test_zero_length_get_set_values
buffer = IO::Buffer.new(0)