mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
Prevent uninitialized classes from being allocated
This commit is contained in:
1 parent
540eaae321
commit
0adee861cb
6 files changed
+100
-83
Notes:
git
2026-10-14 00:03:21 +00:00
No files matched your search
@@ -1277,20 +1277,6 @@ assert_equal 'Ractor::IsolationError', %q{
|
||||
res
|
||||
}
|
||||
|
||||
# Class#initialize writes the superclass of an uninitialized class, so it is
|
||||
# owner-only like any other modification
|
||||
assert_equal 'can not modify K because it is created by another Ractor', %q{
|
||||
K = Class.allocate
|
||||
|
||||
r = Ractor.new { K.send(:initialize, Struct) }
|
||||
|
||||
begin
|
||||
r.join
|
||||
rescue Ractor::RemoteError => e
|
||||
e.cause.message
|
||||
end
|
||||
}
|
||||
|
||||
# Module#refine writes its refinement tables into the receiver, so the receiver
|
||||
# must be owned too
|
||||
assert_equal 'can not modify M because it is created by another Ractor', %q{
|
||||
|
||||
@@ -2200,15 +2200,16 @@ rb_mod_initialize_clone(int argc, VALUE* argv, VALUE clone)
|
||||
* want to treat it like a regular class.
|
||||
*/
|
||||
|
||||
/* :nodoc: */
|
||||
static VALUE
|
||||
rb_class_initialize(int argc, VALUE *argv, VALUE klass)
|
||||
rb_class_s_new(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE super;
|
||||
VALUE klass;
|
||||
|
||||
// an uninitialized class is still somebody's, and this writes its superclass
|
||||
rb_class_modify_check(klass);
|
||||
if (RCLASS_SUPER(klass) != 0 || klass == rb_cBasicObject) {
|
||||
rb_raise(rb_eTypeError, "already initialized class");
|
||||
// #<Class:Class> is an ancestor of every metaclass, so this also receives Foo.singleton_class.new
|
||||
if (self != rb_cClass) {
|
||||
rb_raise(rb_eTypeError, "can't create instance of singleton class");
|
||||
}
|
||||
if (rb_check_arity(argc, 0, 1) == 0) {
|
||||
super = rb_cObject;
|
||||
@@ -2220,6 +2221,7 @@ rb_class_initialize(int argc, VALUE *argv, VALUE klass)
|
||||
rb_raise(rb_eTypeError, "can't inherit uninitialized class");
|
||||
}
|
||||
}
|
||||
klass = rb_class_s_alloc(self);
|
||||
rb_class_set_super(klass, super);
|
||||
RCLASS_SET_MAX_IV_COUNT(klass, RCLASS_MAX_IV_COUNT(super));
|
||||
RCLASS_SET_ALLOCATOR(klass, RCLASS_ALLOCATOR(super));
|
||||
@@ -2231,6 +2233,23 @@ rb_class_initialize(int argc, VALUE *argv, VALUE klass)
|
||||
return klass;
|
||||
}
|
||||
|
||||
/* :nodoc: */
|
||||
static VALUE
|
||||
rb_class_dup(VALUE self)
|
||||
{
|
||||
VALUE dup = rb_class_s_alloc(rb_obj_class(self));
|
||||
return rb_obj_dup_setup(self, dup);
|
||||
}
|
||||
|
||||
/* :nodoc: */
|
||||
static VALUE
|
||||
rb_class_clone(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE kwfreeze = rb_get_freeze_opt(argc, argv);
|
||||
VALUE clone = rb_class_s_alloc(rb_obj_class(self));
|
||||
return rb_obj_clone_setup(self, clone, kwfreeze);
|
||||
}
|
||||
|
||||
/*! \private */
|
||||
void
|
||||
rb_undefined_alloc(VALUE klass)
|
||||
@@ -4691,11 +4710,13 @@ InitVM_Object(void)
|
||||
rb_define_method(rb_singleton_class(rb_cClass), "allocate", rb_class_alloc, 0);
|
||||
rb_define_method(rb_cClass, "allocate", rb_class_alloc, 0);
|
||||
rb_define_method(rb_cClass, "new", rb_class_new_instance_pass_kw, -1);
|
||||
rb_define_method(rb_cClass, "initialize", rb_class_initialize, -1);
|
||||
rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0);
|
||||
rb_define_method(rb_cClass, "subclasses", rb_class_subclasses, 0); /* in class.c */
|
||||
rb_define_method(rb_cClass, "attached_object", rb_class_attached_object, 0); /* in class.c */
|
||||
rb_define_alloc_func(rb_cClass, rb_class_s_alloc);
|
||||
rb_define_singleton_method(rb_cClass, "new", rb_class_s_new, -1);
|
||||
rb_define_method(rb_cClass, "dup", rb_class_dup, 0);
|
||||
rb_define_method(rb_cClass, "clone", rb_class_clone, -1);
|
||||
rb_undef_alloc_func(rb_cClass);
|
||||
rb_undef_method(rb_cClass, "extend_object");
|
||||
rb_undef_method(rb_cClass, "append_features");
|
||||
rb_undef_method(rb_cClass, "prepend_features");
|
||||
|
||||
@@ -6,19 +6,6 @@ describe "Class#allocate" do
|
||||
klass.allocate.should.instance_of?(klass)
|
||||
end
|
||||
|
||||
it "returns a fully-formed instance of Module" do
|
||||
klass = Class.allocate
|
||||
klass.constants.should_not == nil
|
||||
klass.methods.should_not == nil
|
||||
end
|
||||
|
||||
it "throws an exception when calling a method on a new instance" do
|
||||
klass = Class.allocate
|
||||
-> do
|
||||
klass.new
|
||||
end.should.raise(Exception)
|
||||
end
|
||||
|
||||
it "does not call initialize on the new instance" do
|
||||
klass = Class.new do
|
||||
def initialize(*args)
|
||||
@@ -33,9 +20,30 @@ describe "Class#allocate" do
|
||||
klass.allocate.should_not.initialized?
|
||||
end
|
||||
|
||||
it "raises TypeError for #superclass" do
|
||||
-> do
|
||||
Class.allocate.superclass
|
||||
end.should.raise(TypeError)
|
||||
ruby_version_is ""..."4.1" do
|
||||
it "returns a fully-formed instance of Module" do
|
||||
klass = Class.allocate
|
||||
klass.constants.should_not == nil
|
||||
klass.methods.should_not == nil
|
||||
end
|
||||
|
||||
it "throws an exception when calling a method on a new instance" do
|
||||
klass = Class.allocate
|
||||
-> do
|
||||
klass.new
|
||||
end.should.raise(Exception)
|
||||
end
|
||||
|
||||
it "raises TypeError for #superclass" do
|
||||
-> do
|
||||
Class.allocate.superclass
|
||||
end.should.raise(TypeError)
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "4.1" do
|
||||
it "raises TypeError when called on Class itself" do
|
||||
-> { Class.allocate }.should.raise(TypeError)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,34 +1,45 @@
|
||||
require_relative '../../spec_helper'
|
||||
|
||||
describe "Class#initialize" do
|
||||
it "is private" do
|
||||
Class.private_methods(false).should.include?(:initialize)
|
||||
end
|
||||
|
||||
it "raises a TypeError when called on already initialized classes" do
|
||||
->{
|
||||
Integer.send :initialize
|
||||
}.should.raise(TypeError)
|
||||
|
||||
->{
|
||||
Object.send :initialize
|
||||
}.should.raise(TypeError)
|
||||
end
|
||||
|
||||
# See [redmine:2601]
|
||||
it "raises a TypeError when called on BasicObject" do
|
||||
->{
|
||||
BasicObject.send :initialize
|
||||
}.should.raise(TypeError)
|
||||
end
|
||||
|
||||
describe "when given the Class" do
|
||||
before :each do
|
||||
@uninitialized = Class.allocate
|
||||
ruby_version_is ""..."4.1" do
|
||||
describe "Class#initialize" do
|
||||
it "is private" do
|
||||
Class.private_methods(false).should.include?(:initialize)
|
||||
end
|
||||
|
||||
it "raises a TypeError" do
|
||||
->{@uninitialized.send(:initialize, Class)}.should.raise(TypeError)
|
||||
it "raises a TypeError when called on already initialized classes" do
|
||||
->{
|
||||
Integer.send :initialize
|
||||
}.should.raise(TypeError)
|
||||
|
||||
->{
|
||||
Object.send :initialize
|
||||
}.should.raise(TypeError)
|
||||
end
|
||||
|
||||
# See [redmine:2601]
|
||||
it "raises a TypeError when called on BasicObject" do
|
||||
->{
|
||||
BasicObject.send :initialize
|
||||
}.should.raise(TypeError)
|
||||
end
|
||||
|
||||
describe "when given the Class" do
|
||||
before :each do
|
||||
@uninitialized = Class.allocate
|
||||
end
|
||||
|
||||
it "raises a TypeError" do
|
||||
->{@uninitialized.send(:initialize, Class)}.should.raise(TypeError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "4.1" do
|
||||
describe "Class#initialize" do
|
||||
it "is not defined" do
|
||||
Class.private_instance_methods(false).should_not.include?(:initialize)
|
||||
Class.instance_methods(false).should_not.include?(:initialize)
|
||||
end
|
||||
end
|
||||
end
|
||||
+6
-18
@@ -83,12 +83,6 @@ class TestClass < Test::Unit::TestCase
|
||||
assert_nil(Class.new <=> 1)
|
||||
end
|
||||
|
||||
def test_class_initialize
|
||||
assert_raise(TypeError) do
|
||||
Class.new.instance_eval { initialize }
|
||||
end
|
||||
end
|
||||
|
||||
def test_instantiate_singleton_class
|
||||
c = class << Object.new; self; end
|
||||
assert_raise(TypeError) { c.new }
|
||||
@@ -317,24 +311,18 @@ class TestClass < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_uninitialized
|
||||
assert_raise(TypeError) { Class.allocate.new }
|
||||
assert_raise(TypeError) { Class.allocate.superclass }
|
||||
bug6863 = '[ruby-core:47148]'
|
||||
assert_raise(TypeError, bug6863) { Class.new(Class.allocate) }
|
||||
assert_raise(TypeError) { Class.allocate }
|
||||
|
||||
allocator = Class.instance_method(:allocate)
|
||||
assert_raise(TypeError) { allocator.bind_call(Class) }
|
||||
assert_nothing_raised { allocator.bind(Rational).call }
|
||||
assert_nothing_raised { allocator.bind_call(Rational) }
|
||||
end
|
||||
|
||||
def test_uninitialized_with_singleton_class
|
||||
c = Class.allocate
|
||||
c.singleton_class
|
||||
assert_raise(TypeError) { c.new }
|
||||
end
|
||||
|
||||
def test_uninitialized_dup
|
||||
assert_raise(TypeError) { Class.allocate.dup.new }
|
||||
def test_singleton_class_new
|
||||
assert_raise_with_message(TypeError, /singleton class/) { Integer.singleton_class.new }
|
||||
assert_raise_with_message(TypeError, /singleton class/) { Class.new.singleton_class.new }
|
||||
assert_raise_with_message(TypeError, /singleton class/) { Class.singleton_class.new }
|
||||
end
|
||||
|
||||
def test_nonascii_name
|
||||
|
||||
@@ -42,6 +42,9 @@ test_TOPDIR(RbConfigSingletonTest) `TOPDIR` is `nil` during CI while RBS type is
|
||||
# Failing because ObjectSpace.count_nodes has been removed
|
||||
test_count_nodes(ObjectSpaceTest)
|
||||
|
||||
# Class#initialize has been removed
|
||||
test_initialize(ClassInstanceTest)
|
||||
|
||||
## Unknown failures
|
||||
|
||||
# NoMethodError: undefined method 'inspect' for an instance of RBS::UnitTest::Convertibles::ToInt
|
||||
|
||||
Reference in new issue
Block a user