1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/class.c
matz 2f8d3bdc21 * array.c (rb_ary_modify): should copy the internal buffer if the
modifying buffer is shared.

* array.c (ary_make_shared): make an internal buffer of an array
  to be shared.

* array.c (rb_ary_shift): avoid sliding an internal buffer by
  using shared buffer.

* array.c (rb_ary_subseq): avoid copying the buffer.

* parse.y (gettable): should freeze __LINE__ string.

* io.c (rb_io_puts): old behavoir restored.  rationale: a) if you
  want to call to_s for arrays, you can just call print a, "\n".
  b) to_s wastes memory if array (and sum of its contents) is
  huge.  c) now any object that has to_ary is treated as an array,
  using rb_check_convert_type().

* hash.c (rb_hash_initialize): now accepts a block to calculate
  the default value. [new]

* hash.c (rb_hash_aref): call "default" method to get the value
  corrensponding to the non existing key.

* hash.c (rb_hash_default): get the default value based on the
  block given to 'new'.  Now it takes an optinal "key" argument.
  "default" became the method to get the value for non existing
  key.  Users may override "default" method to change the hash
  behavior.

* hash.c (rb_hash_set_default): clear the flag if a block is given
  to 'new'

* object.c (Init_Object): undef Data.allocate, left Data.new.

* ext/curses/curses.c (window_scrollok): use RTEST().

* ext/curses/curses.c (window_idlok): ditto.

* ext/curses/curses.c (window_keypad): ditto.

* ext/curses/curses.c (window_idlok): idlok() may return void on
  some platforms; so don't use return value.

* ext/curses/curses.c (window_scrollok): ditto for consistency.

* ext/curses/curses.c: replace FIX2INT() by typechecking NUM2INT().

* parse.y (str_extend): should not process immature #$x and
  #@x interpolation, e.g #@#@ etc.

* enum.c (enum_sort_by): sort_by does not have to be stable always.

* enum.c (enum_sort_by): call qsort directly to gain performance.

* util.c (ruby_qsort): ruby_qsort(qs6) is now native thread safe.

* error.c (rb_sys_fail): it must be a bug if it's called when
  errno == 0.

* regex.c (WC2MBC1ST): should not pass through > 0x80 number in UTF-8.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1896 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2001-12-10 07:18:16 +00:00

801 lines
16 KiB
C

/**********************************************************************
class.c -
$Author$
$Date$
created at: Tue Aug 10 15:05:44 JST 1993
Copyright (C) 1993-2001 Yukihiro Matsumoto
**********************************************************************/
#include "ruby.h"
#include "rubysig.h"
#include "node.h"
#include "st.h"
#include <ctype.h>
extern st_table *rb_class_tbl;
VALUE
rb_class_boot(super)
VALUE super;
{
NEWOBJ(klass, struct RClass);
OBJSETUP(klass, rb_cClass, T_CLASS);
klass->super = super;
klass->iv_tbl = 0;
klass->m_tbl = 0; /* safe GC */
klass->m_tbl = st_init_numtable();
OBJ_INFECT(klass, super);
return (VALUE)klass;
}
VALUE
rb_class_new(super)
VALUE super;
{
Check_Type(super, T_CLASS);
if (super == rb_cClass) {
rb_raise(rb_eTypeError, "can't make subclass of Class");
}
if (FL_TEST(super, FL_SINGLETON)) {
rb_raise(rb_eTypeError, "can't make subclass of virtual class");
}
return rb_class_boot(super);
}
static int
clone_method(mid, body, tbl)
ID mid;
NODE *body;
st_table *tbl;
{
st_insert(tbl, mid, NEW_METHOD(body->nd_body, body->nd_noex));
return ST_CONTINUE;
}
VALUE
rb_mod_clone(module)
VALUE module;
{
VALUE clone = rb_obj_clone(module);
RCLASS(clone)->super = RCLASS(module)->super;
if (RCLASS(module)->iv_tbl) {
ID id;
RCLASS(clone)->iv_tbl = st_copy(RCLASS(module)->iv_tbl);
id = rb_intern("__classpath__");
st_delete(RCLASS(clone)->iv_tbl, &id, 0);
id = rb_intern("__classid__");
st_delete(RCLASS(clone)->iv_tbl, &id, 0);
}
if (RCLASS(module)->m_tbl) {
RCLASS(clone)->m_tbl = st_init_numtable();
st_foreach(RCLASS(module)->m_tbl, clone_method, RCLASS(clone)->m_tbl);
}
return clone;
}
VALUE
rb_mod_dup(mod)
VALUE mod;
{
VALUE dup = rb_mod_clone(mod);
DUPSETUP(dup, mod);
if (FL_TEST(mod, FL_SINGLETON)) {
FL_SET(dup, FL_SINGLETON);
}
return dup;
}
VALUE
rb_singleton_class_new(super)
VALUE super;
{
VALUE klass = rb_class_boot(super);
FL_SET(klass, FL_SINGLETON);
return klass;
}
VALUE
rb_singleton_class_clone(klass)
VALUE klass;
{
if (!FL_TEST(klass, FL_SINGLETON))
return klass;
else {
/* copy singleton(unnamed) class */
NEWOBJ(clone, struct RClass);
CLONESETUP(clone, klass);
clone->super = RCLASS(klass)->super;
clone->iv_tbl = 0;
clone->m_tbl = 0;
if (RCLASS(klass)->iv_tbl) {
clone->iv_tbl = st_copy(RCLASS(klass)->iv_tbl);
}
clone->m_tbl = st_init_numtable();
st_foreach(RCLASS(klass)->m_tbl, clone_method, clone->m_tbl);
FL_SET(clone, FL_SINGLETON);
return (VALUE)clone;
}
}
void
rb_singleton_class_attached(klass, obj)
VALUE klass, obj;
{
if (FL_TEST(klass, FL_SINGLETON)) {
if (!RCLASS(klass)->iv_tbl) {
RCLASS(klass)->iv_tbl = st_init_numtable();
}
st_insert(RCLASS(klass)->iv_tbl, rb_intern("__attached__"), obj);
}
}
VALUE
rb_define_class_id(id, super)
ID id;
VALUE super;
{
VALUE klass;
if (!super) super = rb_cObject;
klass = rb_class_new(super);
rb_name_class(klass, id);
/* make metaclass */
RBASIC(klass)->klass = rb_singleton_class_new(RBASIC(super)->klass);
rb_singleton_class_attached(RBASIC(klass)->klass, klass);
rb_funcall(super, rb_intern("inherited"), 1, klass);
return klass;
}
VALUE
rb_define_class(name, super)
const char *name;
VALUE super;
{
VALUE klass;
ID id;
id = rb_intern(name);
if (rb_const_defined(rb_cObject, id)) {
klass = rb_const_get(rb_cObject, id);
rb_name_error(id, "%s is already defined", name);
}
klass = rb_define_class_id(id, super);
st_add_direct(rb_class_tbl, id, klass);
return klass;
}
VALUE
rb_define_class_under(outer, name, super)
VALUE outer;
const char *name;
VALUE super;
{
VALUE klass;
ID id;
id = rb_intern(name);
if (rb_const_defined_at(outer, id)) {
klass = rb_const_get(outer, id);
rb_name_error(id, "%s is already defined", name);
}
klass = rb_define_class_id(id, super);
rb_const_set(outer, id, klass);
rb_set_class_path(klass, outer, name);
return klass;
}
VALUE
rb_module_new()
{
NEWOBJ(mdl, struct RClass);
OBJSETUP(mdl, rb_cModule, T_MODULE);
mdl->super = 0;
mdl->iv_tbl = 0;
mdl->m_tbl = 0;
mdl->m_tbl = st_init_numtable();
return (VALUE)mdl;
}
VALUE
rb_define_module_id(id)
ID id;
{
VALUE mdl;
mdl = rb_module_new();
rb_name_class(mdl, id);
return mdl;
}
VALUE
rb_define_module(name)
const char *name;
{
VALUE module;
ID id;
id = rb_intern(name);
if (rb_const_defined(rb_cObject, id)) {
module = rb_const_get(rb_cObject, id);
if (TYPE(module) == T_MODULE)
return module;
rb_raise(rb_eTypeError, "%s is not a module", rb_class2name(CLASS_OF(module)));
}
module = rb_define_module_id(id);
st_add_direct(rb_class_tbl, id, module);
return module;
}
VALUE
rb_define_module_under(outer, name)
VALUE outer;
const char *name;
{
VALUE module;
ID id;
id = rb_intern(name);
if (rb_const_defined(outer, id)) {
module = rb_const_get(rb_cObject, id);
if (TYPE(module) == T_MODULE)
return module;
rb_raise(rb_eTypeError, "%s::%s is not a module",
rb_class2name(outer), rb_class2name(CLASS_OF(module)));
}
module = rb_define_module_id(id);
rb_const_set(outer, id, module);
rb_set_class_path(module, outer, name);
return module;
}
static VALUE
include_class_new(module, super)
VALUE module, super;
{
NEWOBJ(klass, struct RClass);
OBJSETUP(klass, rb_cClass, T_ICLASS);
if (!RCLASS(module)->iv_tbl) {
RCLASS(module)->iv_tbl = st_init_numtable();
}
klass->iv_tbl = RCLASS(module)->iv_tbl;
klass->m_tbl = RCLASS(module)->m_tbl;
klass->super = super;
if (TYPE(module) == T_ICLASS) {
RBASIC(klass)->klass = RBASIC(module)->klass;
}
else {
RBASIC(klass)->klass = module;
}
OBJ_INFECT(klass, module);
OBJ_INFECT(klass, super);
return (VALUE)klass;
}
void
rb_include_module(klass, module)
VALUE klass, module;
{
VALUE p, c;
int changed = 0;
rb_frozen_class_p(klass);
if (!OBJ_TAINTED(klass)) {
rb_secure(4);
}
if (NIL_P(module)) return;
if (klass == module) return;
switch (TYPE(module)) {
case T_MODULE:
case T_CLASS:
case T_ICLASS:
break;
default:
Check_Type(module, T_MODULE);
}
OBJ_INFECT(klass, module);
c = klass;
while (module) {
/* ignore if the module included already in superclasses */
for (p = RCLASS(klass)->super; p; p = RCLASS(p)->super) {
if (BUILTIN_TYPE(p) == T_ICLASS &&
RCLASS(p)->m_tbl == RCLASS(module)->m_tbl) {
goto skip;
}
}
RCLASS(c)->super = include_class_new(module, RCLASS(c)->super);
c = RCLASS(c)->super;
changed = 1;
skip:
module = RCLASS(module)->super;
}
if (changed) rb_clear_cache();
}
VALUE
rb_mod_included_modules(mod)
VALUE mod;
{
VALUE ary = rb_ary_new();
VALUE p;
for (p = RCLASS(mod)->super; p; p = RCLASS(p)->super) {
if (BUILTIN_TYPE(p) == T_ICLASS) {
rb_ary_push(ary, RBASIC(p)->klass);
}
}
return ary;
}
VALUE
rb_mod_include_p(mod, mod2)
VALUE mod;
VALUE mod2;
{
VALUE p;
Check_Type(mod2, T_MODULE);
for (p = RCLASS(mod)->super; p; p = RCLASS(p)->super) {
if (BUILTIN_TYPE(p) == T_ICLASS) {
if (RBASIC(p)->klass == mod2) return Qtrue;
}
}
return Qfalse;
}
VALUE
rb_mod_ancestors(mod)
VALUE mod;
{
VALUE ary = rb_ary_new();
VALUE p;
for (p = mod; p; p = RCLASS(p)->super) {
if (FL_TEST(p, FL_SINGLETON))
continue;
if (BUILTIN_TYPE(p) == T_ICLASS) {
rb_ary_push(ary, RBASIC(p)->klass);
}
else {
rb_ary_push(ary, p);
}
}
return ary;
}
static int
ins_methods_i(key, body, ary)
ID key;
NODE *body;
VALUE ary;
{
if ((body->nd_noex&(NOEX_PRIVATE|NOEX_PROTECTED)) == 0) {
VALUE name = rb_str_new2(rb_id2name(key));
if (!rb_ary_includes(ary, name)) {
if (!body->nd_body) {
rb_ary_push(ary, Qnil);
}
rb_ary_push(ary, name);
}
}
else if (body->nd_body && nd_type(body->nd_body) == NODE_ZSUPER) {
rb_ary_push(ary, Qnil);
rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
}
return ST_CONTINUE;
}
static int
ins_methods_prot_i(key, body, ary)
ID key;
NODE *body;
VALUE ary;
{
if (!body->nd_body) {
rb_ary_push(ary, Qnil);
rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
}
else if (body->nd_noex & NOEX_PROTECTED) {
VALUE name = rb_str_new2(rb_id2name(key));
if (!rb_ary_includes(ary, name)) {
rb_ary_push(ary, name);
}
}
else if (nd_type(body->nd_body) == NODE_ZSUPER) {
rb_ary_push(ary, Qnil);
rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
}
return ST_CONTINUE;
}
static int
ins_methods_priv_i(key, body, ary)
ID key;
NODE *body;
VALUE ary;
{
if (!body->nd_body) {
rb_ary_push(ary, Qnil);
rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
}
else if (body->nd_noex & NOEX_PRIVATE) {
VALUE name = rb_str_new2(rb_id2name(key));
if (!rb_ary_includes(ary, name)) {
rb_ary_push(ary, name);
}
}
else if (nd_type(body->nd_body) == NODE_ZSUPER) {
rb_ary_push(ary, Qnil);
rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
}
return ST_CONTINUE;
}
static VALUE
method_list(mod, option, func)
VALUE mod;
int option;
int (*func)();
{
VALUE ary;
VALUE klass;
VALUE *p, *q, *pend;
ary = rb_ary_new();
for (klass = mod; klass; klass = RCLASS(klass)->super) {
st_foreach(RCLASS(klass)->m_tbl, func, ary);
if (!option) break;
}
p = q = RARRAY(ary)->ptr; pend = p + RARRAY(ary)->len;
while (p < pend) {
if (*p == Qnil) {
p+=2;
continue;
}
*q++ = *p++;
}
RARRAY(ary)->len = q - RARRAY(ary)->ptr;
return ary;
}
VALUE
rb_class_instance_methods(argc, argv, mod)
int argc;
VALUE *argv;
VALUE mod;
{
VALUE option;
rb_scan_args(argc, argv, "01", &option);
return method_list(mod, RTEST(option), ins_methods_i);
}
VALUE
rb_class_protected_instance_methods(argc, argv, mod)
int argc;
VALUE *argv;
VALUE mod;
{
VALUE option;
rb_scan_args(argc, argv, "01", &option);
return method_list(mod, RTEST(option), ins_methods_prot_i);
}
VALUE
rb_class_private_instance_methods(argc, argv, mod)
int argc;
VALUE *argv;
VALUE mod;
{
VALUE option;
rb_scan_args(argc, argv, "01", &option);
return method_list(mod, RTEST(option), ins_methods_priv_i);
}
VALUE
rb_obj_singleton_methods(argc, argv, obj)
int argc;
VALUE *argv;
VALUE obj;
{
VALUE all;
VALUE ary;
VALUE klass;
VALUE *p, *q, *pend;
rb_scan_args(argc, argv, "01", &all);
ary = rb_ary_new();
klass = CLASS_OF(obj);
while (klass && FL_TEST(klass, FL_SINGLETON)) {
st_foreach(RCLASS(klass)->m_tbl, ins_methods_i, ary);
klass = RCLASS(klass)->super;
}
if (RTEST(all)) {
while (klass && TYPE(klass) == T_ICLASS) {
st_foreach(RCLASS(klass)->m_tbl, ins_methods_i, ary);
klass = RCLASS(klass)->super;
}
}
p = q = RARRAY(ary)->ptr; pend = p + RARRAY(ary)->len;
while (p < pend) {
if (*p == Qnil) {
p+=2;
continue;
}
*q++ = *p++;
}
RARRAY(ary)->len = q - RARRAY(ary)->ptr;
return ary;
}
void
rb_define_method_id(klass, name, func, argc)
VALUE klass;
ID name;
VALUE (*func)();
int argc;
{
rb_add_method(klass, name, NEW_CFUNC(func,argc), NOEX_PUBLIC|NOEX_CFUNC);
}
void
rb_define_method(klass, name, func, argc)
VALUE klass;
const char *name;
VALUE (*func)();
int argc;
{
ID id = rb_intern(name);
rb_add_method(klass, id, NEW_CFUNC(func, argc),
((name[0] == 'i' && id == rb_intern("initialize"))?
NOEX_PRIVATE:NOEX_PUBLIC)|NOEX_CFUNC);
}
void
rb_define_protected_method(klass, name, func, argc)
VALUE klass;
const char *name;
VALUE (*func)();
int argc;
{
rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc),
NOEX_PROTECTED|NOEX_CFUNC);
}
void
rb_define_private_method(klass, name, func, argc)
VALUE klass;
const char *name;
VALUE (*func)();
int argc;
{
rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc),
NOEX_PRIVATE|NOEX_CFUNC);
}
void
rb_undef_method(klass, name)
VALUE klass;
const char *name;
{
rb_add_method(klass, rb_intern(name), 0, NOEX_UNDEF);
}
#define SPECIAL_SINGLETON(x,c) if (obj == (x)) {\
if (!FL_TEST(c, FL_SINGLETON)) {\
c = rb_singleton_class_new(c);\
rb_singleton_class_attached(c,obj);\
}\
return c;\
}
VALUE
rb_singleton_class(obj)
VALUE obj;
{
VALUE klass;
if (FIXNUM_P(obj) || SYMBOL_P(obj)) {
rb_raise(rb_eTypeError, "can't define singleton");
}
if (rb_special_const_p(obj)) {
SPECIAL_SINGLETON(Qnil, rb_cNilClass);
SPECIAL_SINGLETON(Qfalse, rb_cFalseClass);
SPECIAL_SINGLETON(Qtrue, rb_cTrueClass);
rb_bug("unknown immediate %d", obj);
}
DEFER_INTS;
if (FL_TEST(RBASIC(obj)->klass, FL_SINGLETON)) {
klass = RBASIC(obj)->klass;
}
else {
klass = rb_singleton_class_new(RBASIC(obj)->klass);
RBASIC(obj)->klass = klass;
rb_singleton_class_attached(klass, obj);
}
if (OBJ_TAINTED(obj)) {
OBJ_TAINT(klass);
}
else {
FL_UNSET(klass, FL_TAINT);
}
if (OBJ_FROZEN(obj)) OBJ_FREEZE(klass);
ALLOW_INTS;
return klass;
}
void
rb_define_singleton_method(obj, name, func, argc)
VALUE obj;
const char *name;
VALUE (*func)();
int argc;
{
rb_define_method(rb_singleton_class(obj), name, func, argc);
}
void
rb_define_module_function(module, name, func, argc)
VALUE module;
const char *name;
VALUE (*func)();
int argc;
{
rb_define_private_method(module, name, func, argc);
rb_define_singleton_method(module, name, func, argc);
}
void
rb_define_global_function(name, func, argc)
const char *name;
VALUE (*func)();
int argc;
{
rb_define_module_function(rb_mKernel, name, func, argc);
}
void
rb_define_alias(klass, name1, name2)
VALUE klass;
const char *name1, *name2;
{
rb_alias(klass, rb_intern(name1), rb_intern(name2));
}
void
rb_define_attr(klass, name, read, write)
VALUE klass;
const char *name;
int read, write;
{
rb_attr(klass, rb_intern(name), read, write, Qfalse);
}
#ifdef HAVE_STDARG_PROTOTYPES
#include <stdarg.h>
#define va_init_list(a,b) va_start(a,b)
#else
#include <varargs.h>
#define va_init_list(a,b) va_start(a)
#endif
int
#ifdef HAVE_STDARG_PROTOTYPES
rb_scan_args(int argc, VALUE *argv, const char *fmt, ...)
#else
rb_scan_args(argc, argv, fmt, va_alist)
int argc;
VALUE *argv;
const char *fmt;
va_dcl
#endif
{
int n, i = 0;
const char *p = fmt;
VALUE *var;
va_list vargs;
va_init_list(vargs, fmt);
if (*p == '*') goto rest_arg;
if (ISDIGIT(*p)) {
n = *p - '0';
if (n > argc)
rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, n);
for (i=0; i<n; i++) {
var = va_arg(vargs, VALUE*);
if (var) *var = argv[i];
}
p++;
}
else {
goto error;
}
if (ISDIGIT(*p)) {
n = i + *p - '0';
for (; i<n; i++) {
var = va_arg(vargs, VALUE*);
if (argc > i) {
if (var) *var = argv[i];
}
else {
if (var) *var = Qnil;
}
}
p++;
}
if(*p == '*') {
rest_arg:
var = va_arg(vargs, VALUE*);
if (argc > i) {
if (var) *var = rb_ary_new4(argc-i, argv+i);
i = argc;
}
else {
if (var) *var = rb_ary_new();
}
p++;
}
if (*p == '&') {
var = va_arg(vargs, VALUE*);
if (rb_block_given_p()) {
*var = rb_f_lambda();
}
else {
*var = Qnil;
}
p++;
}
va_end(vargs);
if (*p != '\0') {
goto error;
}
if (argc > i) {
rb_raise(rb_eArgError, "wrong number of arguments(%d for %d)", argc, i);
}
return argc;
error:
rb_fatal("bad scan arg format: %s", fmt);
return 0;
}