mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
Merge remote-tracking branch 'upstream/master' into mrbgems
This commit is contained in:
+31
-16
@@ -13,6 +13,9 @@
|
||||
/* add -DMRB_USE_FLOAT to use float instead of double for floating point numbers */
|
||||
//#define MRB_USE_FLOAT
|
||||
|
||||
/* add -DMRB_INT64 to use 64bit integer for mrb_int */
|
||||
//#define MRB_INT64
|
||||
|
||||
/* represent mrb_value in boxed double; conflict with MRB_USE_FLOAT */
|
||||
//#define MRB_NAN_BOXING
|
||||
|
||||
@@ -54,27 +57,39 @@
|
||||
/* end of configuration */
|
||||
|
||||
#ifdef MRB_USE_FLOAT
|
||||
typedef float mrb_float;
|
||||
#define mrb_float_to_str(buf, i) sprintf((buf), "%.7e", (i))
|
||||
#define str_to_mrb_float(buf) (mrb_float)strtof((buf),NULL)
|
||||
typedef float mrb_float;
|
||||
# define mrb_float_to_str(buf, i) sprintf((buf), "%.7e", (i))
|
||||
# define str_to_mrb_float(buf) (mrb_float)strtof((buf),NULL)
|
||||
#else
|
||||
typedef double mrb_float;
|
||||
#define mrb_float_to_str(buf, i) sprintf((buf), "%.16e", (i))
|
||||
#define str_to_mrb_float(buf) (mrb_float)strtod((buf),NULL)
|
||||
typedef double mrb_float;
|
||||
# define mrb_float_to_str(buf, i) sprintf((buf), "%.16e", (i))
|
||||
# define str_to_mrb_float(buf) (mrb_float)strtod((buf),NULL)
|
||||
#endif
|
||||
|
||||
#ifdef MRB_NAN_BOXING
|
||||
typedef int32_t mrb_int;
|
||||
#define MRB_INT_MIN INT32_MIN
|
||||
#define MRB_INT_MAX INT32_MAX
|
||||
#define mrb_int_to_str(buf, i) sprintf((buf), "%d", (i))
|
||||
#define str_to_mrb_int(buf) (mrb_int)strtol((buf), NULL, 10);
|
||||
# ifdef MRB_INT64
|
||||
# error Cannot use NaN boxing when mrb_int is 64bit
|
||||
# else
|
||||
typedef int32_t mrb_int;
|
||||
# define MRB_INT_MIN INT32_MIN
|
||||
# define MRB_INT_MAX INT32_MAX
|
||||
# define mrb_int_to_str(buf, i) sprintf((buf), "%d", (i))
|
||||
# define str_to_mrb_int(buf) (mrb_int)strtol((buf), NULL, 10)
|
||||
# endif
|
||||
#else
|
||||
typedef int mrb_int;
|
||||
#define MRB_INT_MIN INT_MIN
|
||||
#define MRB_INT_MAX INT_MAX
|
||||
#define mrb_int_to_str(buf, i) sprintf((buf), "%d", (i))
|
||||
#define str_to_mrb_int(buf) (mrb_int)strtol((buf), NULL, 10);
|
||||
# ifdef MRB_INT64
|
||||
typedef int64_t mrb_int;
|
||||
# define MRB_INT_MIN INT64_MIN
|
||||
# define MRB_INT_MAX INT64_MAX
|
||||
# define mrb_int_to_str(buf, i) sprintf((buf), "%ld", (i))
|
||||
# define str_to_mrb_int(buf) (mrb_int)strtoll((buf), NULL, 10)
|
||||
# else
|
||||
typedef int mrb_int;
|
||||
# define MRB_INT_MIN INT_MIN
|
||||
# define MRB_INT_MAX INT_MAX
|
||||
# define mrb_int_to_str(buf, i) sprintf((buf), "%d", (i))
|
||||
# define str_to_mrb_int(buf) (mrb_int)strtol((buf), NULL, 10)
|
||||
# endif
|
||||
#endif
|
||||
typedef short mrb_sym;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key);
|
||||
mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def);
|
||||
mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key);
|
||||
mrb_value mrb_hash(mrb_state *mrb, mrb_value obj);
|
||||
mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash);
|
||||
|
||||
/* RHASH_TBL allocates st_table if not available. */
|
||||
#define RHASH(obj) ((struct RHash*)((obj).value.p))
|
||||
|
||||
@@ -53,6 +53,7 @@ void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value val);
|
||||
mrb_value mrb_obj_instance_variables(mrb_state*, mrb_value);
|
||||
mrb_value mrb_obj_iv_inspect(mrb_state*, struct RObject*);
|
||||
mrb_sym mrb_class_sym(mrb_state *mrb, struct RClass *c, struct RClass *outer);
|
||||
mrb_value mrb_mod_class_variables(mrb_state*, mrb_value);
|
||||
|
||||
/* GC functions */
|
||||
void mrb_gc_mark_gv(mrb_state*);
|
||||
|
||||
+1
-1
@@ -772,7 +772,7 @@ mrb_value
|
||||
mrb_ary_first(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
struct RArray *a = mrb_ary_ptr(self);
|
||||
int size;
|
||||
mrb_int size;
|
||||
|
||||
if (mrb_get_args(mrb, "|i", &size) == 0) {
|
||||
return (a->len > 0)? a->ptr[0]: mrb_nil_value();
|
||||
|
||||
+36
-39
@@ -64,25 +64,38 @@ mrb_name_class(mrb_state *mrb, struct RClass *c, mrb_sym name)
|
||||
mrb_intern(mrb, "__classid__"), mrb_symbol_value(name));
|
||||
}
|
||||
|
||||
static void
|
||||
make_metaclass(mrb_state *mrb, struct RClass *c)
|
||||
{
|
||||
struct RClass *sc;
|
||||
#define make_metaclass(mrb, c) prepare_singleton_class((mrb), (struct RBasic*)(c))
|
||||
|
||||
if (c->c->tt == MRB_TT_SCLASS) {
|
||||
return;
|
||||
}
|
||||
static void
|
||||
prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
|
||||
{
|
||||
struct RClass *sc, *c;
|
||||
|
||||
if (o->c->tt == MRB_TT_SCLASS) return;
|
||||
sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class);
|
||||
sc->mt = 0;
|
||||
if (!c->super) {
|
||||
sc->super = mrb->class_class;
|
||||
sc->iv = 0;
|
||||
if (o->tt == MRB_TT_CLASS) {
|
||||
c = (struct RClass*)o;
|
||||
if (!c->super) {
|
||||
sc->super = mrb->class_class;
|
||||
}
|
||||
else {
|
||||
sc->super = c->super->c;
|
||||
}
|
||||
}
|
||||
else {
|
||||
else if (o->tt == MRB_TT_SCLASS) {
|
||||
c = (struct RClass*)o;
|
||||
make_metaclass(mrb, c->super);
|
||||
sc->super = c->super->c;
|
||||
}
|
||||
c->c = sc;
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)sc);
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)sc, (struct RBasic*)sc->super);
|
||||
else {
|
||||
sc->super = o->c;
|
||||
}
|
||||
o->c = sc;
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)o, (struct RBasic*)sc);
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)sc, (struct RBasic*)o);
|
||||
mrb_obj_iv_set(mrb, (struct RObject*)sc, mrb_intern(mrb, "__attached__"), mrb_obj_value(o));
|
||||
}
|
||||
|
||||
struct RClass*
|
||||
@@ -178,7 +191,6 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %s", mrb_sym2name(mrb, id));
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -758,22 +770,6 @@ mrb_mod_included_modules(mrb_state *mrb, mrb_value self)
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct RClass *
|
||||
mrb_singleton_class_ptr(mrb_state *mrb, struct RClass *c)
|
||||
{
|
||||
struct RClass *sc;
|
||||
|
||||
if (c->tt == MRB_TT_SCLASS) {
|
||||
return c;
|
||||
}
|
||||
sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class);
|
||||
sc->mt = 0;
|
||||
sc->super = c;
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)sc, (struct RBasic*)c);
|
||||
|
||||
return sc;
|
||||
}
|
||||
|
||||
mrb_value
|
||||
mrb_singleton_class(mrb_state *mrb, mrb_value v)
|
||||
{
|
||||
@@ -797,14 +793,14 @@ mrb_singleton_class(mrb_state *mrb, mrb_value v)
|
||||
break;
|
||||
}
|
||||
obj = mrb_object(v);
|
||||
obj->c = mrb_singleton_class_ptr(mrb, obj->c);
|
||||
prepare_singleton_class(mrb, obj);
|
||||
return mrb_obj_value(obj->c);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_define_singleton_method(mrb_state *mrb, struct RObject *o, const char *name, mrb_func_t func, int aspec)
|
||||
{
|
||||
o->c = mrb_singleton_class_ptr(mrb, o->c);
|
||||
prepare_singleton_class(mrb, (struct RBasic*)o);
|
||||
mrb_define_method_id(mrb, o->c, mrb_intern(mrb, name), func, aspec);
|
||||
}
|
||||
|
||||
@@ -945,15 +941,14 @@ mrb_value
|
||||
mrb_class_superclass(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
struct RClass *c;
|
||||
mrb_value superclass;
|
||||
|
||||
c = mrb_class_ptr(klass);
|
||||
if (c->super)
|
||||
superclass = mrb_obj_value(mrb_class_real(c->super));
|
||||
else
|
||||
superclass = mrb_nil_value();
|
||||
|
||||
return superclass;
|
||||
c = c->super;
|
||||
while (c && c->tt == MRB_TT_ICLASS) {
|
||||
c = c->super;
|
||||
}
|
||||
if (!c) return mrb_nil_value();
|
||||
return mrb_obj_value(c);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
@@ -1224,6 +1219,7 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass)
|
||||
switch (mrb_type(v)) {
|
||||
case MRB_TT_CLASS:
|
||||
case MRB_TT_MODULE:
|
||||
case MRB_TT_SCLASS:
|
||||
mrb_str_append(mrb, s, mrb_inspect(mrb, v));
|
||||
break;
|
||||
default:
|
||||
@@ -1440,6 +1436,7 @@ mrb_init_class(mrb_state *mrb)
|
||||
mrb_define_method(mrb, mod, "const_get", mrb_mod_const_get, ARGS_REQ(1)); /* 15.2.2.4.21 */
|
||||
mrb_define_method(mrb, mod, "const_set", mrb_mod_const_set, ARGS_REQ(2)); /* 15.2.2.4.23 */
|
||||
mrb_define_method(mrb, mod, "define_method", mod_define_method, ARGS_REQ(1));
|
||||
mrb_define_method(mrb, mod, "class_variables", mrb_mod_class_variables, ARGS_NONE()); /* 15.2.2.4.19 */
|
||||
|
||||
mrb_define_method(mrb, mod, "===", mrb_mod_eqq, ARGS_REQ(1));
|
||||
mrb_undef_method(mrb, cls, "append_features");
|
||||
|
||||
+1
-1
@@ -965,7 +965,7 @@ mrb_hash_to_hash(mrb_state *mrb, mrb_value hash)
|
||||
*
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
mrb_value
|
||||
mrb_hash_keys(mrb_state *mrb, mrb_value hash)
|
||||
{
|
||||
khash_t(ht) *h = RHASH_TBL(hash);
|
||||
|
||||
+5
-1
@@ -287,7 +287,8 @@ mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj)
|
||||
|
||||
clone->super = klass->super;
|
||||
if (klass->iv) {
|
||||
clone->iv = klass->iv;
|
||||
mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass));
|
||||
mrb_obj_iv_set(mrb, (struct RObject*)clone, mrb_intern(mrb, "__attached__"), obj);
|
||||
}
|
||||
if (klass->mt) {
|
||||
clone->mt = kh_copy(mt, mrb, klass->mt);
|
||||
@@ -307,6 +308,9 @@ init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
|
||||
case MRB_TT_OBJECT:
|
||||
case MRB_TT_CLASS:
|
||||
case MRB_TT_MODULE:
|
||||
case MRB_TT_SCLASS:
|
||||
case MRB_TT_HASH:
|
||||
case MRB_TT_DATA:
|
||||
mrb_iv_copy(mrb, dest, obj);
|
||||
break;
|
||||
|
||||
|
||||
+2
-1
@@ -523,7 +523,8 @@ static mrb_value
|
||||
flo_round(mrb_state *mrb, mrb_value num)
|
||||
{
|
||||
double number, f;
|
||||
int ndigits = 0, i;
|
||||
mrb_int ndigits = 0;
|
||||
int i;
|
||||
|
||||
mrb_get_args(mrb, "|i", &ndigits);
|
||||
number = mrb_float(num);
|
||||
|
||||
+1
-1
@@ -1749,7 +1749,7 @@ aref_args : none
|
||||
}
|
||||
| assocs trailer
|
||||
{
|
||||
$$ = new_hash(p, $1);
|
||||
$$ = cons(new_hash(p, $1), 0);
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
+1
-1
@@ -286,7 +286,7 @@ mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp,
|
||||
if (end > len) end = len;
|
||||
}
|
||||
if (end < 0) end += len;
|
||||
if (!r->excl) end++; /* include end point */
|
||||
if (!r->excl && end < len) end++; /* include end point */
|
||||
len = end - beg;
|
||||
if (len < 0) len = 0;
|
||||
|
||||
|
||||
+1
-1
@@ -671,7 +671,7 @@ retry:
|
||||
tmp = mrb_check_string_type(mrb, val);
|
||||
if (!mrb_nil_p(tmp)) {
|
||||
if (RSTRING_LEN(tmp) != 1 ) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "%%c requires a character");
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character");
|
||||
}
|
||||
c = RSTRING_PTR(tmp)[0];
|
||||
n = 1;
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ mrb_add_irep(mrb_state *mrb, int idx)
|
||||
mrb->irep_capa *= 2;
|
||||
}
|
||||
mrb->irep = (mrb_irep **)mrb_realloc(mrb, mrb->irep, sizeof(mrb_irep*)*mrb->irep_capa);
|
||||
for (i = old_capa; i < mrb->irep_capa - old_capa; i++) {
|
||||
for (i = old_capa; i < mrb->irep_capa; i++) {
|
||||
mrb->irep[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -2125,7 +2125,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
|
||||
mrb_value spat = mrb_nil_value();
|
||||
enum {awk, string, regexp} split_type = string;
|
||||
long beg, end, i = 0;
|
||||
int lim = -1;
|
||||
mrb_int lim = -1;
|
||||
mrb_value result, tmp;
|
||||
|
||||
argc = mrb_get_args(mrb, "|oi", &spat, &lim);
|
||||
|
||||
+48
-4
@@ -71,13 +71,13 @@ iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
|
||||
t->size++;
|
||||
return;
|
||||
}
|
||||
if (key == 0 && !matched_seg) {
|
||||
if (!matched_seg && key == 0) {
|
||||
matched_seg = seg;
|
||||
matched_idx = i;
|
||||
}
|
||||
else if (key == sym) {
|
||||
seg->val[i] = val;
|
||||
return;
|
||||
seg->val[i] = val;
|
||||
return;
|
||||
}
|
||||
}
|
||||
prev = seg;
|
||||
@@ -223,10 +223,10 @@ iv_copy(mrb_state *mrb, iv_tbl *t)
|
||||
mrb_sym key = seg->key[i];
|
||||
mrb_value val = seg->val[i];
|
||||
|
||||
iv_put(mrb, t2, key, val);
|
||||
if ((seg->next == NULL) && (i >= t->last_len)) {
|
||||
return t2;
|
||||
}
|
||||
iv_put(mrb, t2, key, val);
|
||||
}
|
||||
seg = seg->next;
|
||||
}
|
||||
@@ -615,6 +615,50 @@ mrb_obj_instance_variables(mrb_state *mrb, mrb_value self)
|
||||
return ary;
|
||||
}
|
||||
|
||||
static int
|
||||
cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
|
||||
{
|
||||
mrb_value ary;
|
||||
const char* s;
|
||||
int len;
|
||||
|
||||
ary = *(mrb_value*)p;
|
||||
s = mrb_sym2name_len(mrb, sym, &len);
|
||||
if (len > 2 && s[0] == '@' && s[1] == '@') {
|
||||
mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 15.2.2.4.19 */
|
||||
/*
|
||||
* call-seq:
|
||||
* mod.class_variables -> array
|
||||
*
|
||||
* Returns an array of the names of class variables in <i>mod</i>.
|
||||
*
|
||||
* class One
|
||||
* @@var1 = 1
|
||||
* end
|
||||
* class Two < One
|
||||
* @@var2 = 2
|
||||
* end
|
||||
* One.class_variables #=> [:@@var1]
|
||||
* Two.class_variables #=> [:@@var2]
|
||||
*/
|
||||
mrb_value
|
||||
mrb_mod_class_variables(mrb_state *mrb, mrb_value mod)
|
||||
{
|
||||
mrb_value ary;
|
||||
|
||||
ary = mrb_ary_new(mrb);
|
||||
if (obj_iv_p(mod) && mrb_obj_ptr(mod)->iv) {
|
||||
iv_foreach(mrb, mrb_obj_ptr(mod)->iv, cv_i, &ary);
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
|
||||
|
||||
mrb_value
|
||||
mrb_vm_cv_get(mrb_state *mrb, mrb_sym sym)
|
||||
{
|
||||
|
||||
@@ -151,6 +151,18 @@ uvset(mrb_state *mrb, int up, int idx, mrb_value v)
|
||||
mrb_write_barrier(mrb, (struct RBasic*)e);
|
||||
}
|
||||
|
||||
struct REnv*
|
||||
top_env(struct RProc *proc)
|
||||
{
|
||||
struct REnv *e = proc->env;
|
||||
|
||||
while (e->c) {
|
||||
if (!e) return 0;
|
||||
e = (struct REnv*)e->c;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
static mrb_callinfo*
|
||||
cipush(mrb_state *mrb)
|
||||
{
|
||||
@@ -1153,11 +1165,15 @@ mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self)
|
||||
break;
|
||||
case OP_R_RETURN:
|
||||
if (!proc->env) goto NORMAL_RETURN;
|
||||
if (proc->env->cioff < 0) {
|
||||
localjump_error(mrb, "return");
|
||||
goto L_RAISE;
|
||||
}
|
||||
ci = mrb->ci = mrb->cibase + proc->env->cioff;
|
||||
else {
|
||||
struct REnv *e = top_env(proc);
|
||||
|
||||
if (e->cioff < 0) {
|
||||
localjump_error(mrb, "return");
|
||||
goto L_RAISE;
|
||||
}
|
||||
ci = mrb->ci = mrb->cibase + e->cioff;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* cannot happen */
|
||||
|
||||
@@ -388,3 +388,17 @@ assert('BS Block [ruby-core:14395]') do
|
||||
t = Controller.new
|
||||
t.test_for_bug
|
||||
end
|
||||
|
||||
assert("BS Block 32") do
|
||||
module TestReturnFromNestedBlock
|
||||
def self.test
|
||||
1.times do
|
||||
1.times do
|
||||
return :ok
|
||||
end
|
||||
end
|
||||
:bad
|
||||
end
|
||||
end
|
||||
TestReturnFromNestedBlock.test == :ok
|
||||
end
|
||||
|
||||
@@ -31,6 +31,18 @@ assert('Module#append_features', '15.2.2.4.10') do
|
||||
Test4AppendFeatures2.const_get(:Const4AppendFeatures2) == Test4AppendFeatures2
|
||||
end
|
||||
|
||||
assert('Module#class_variables', '15.2.2.4.19') do
|
||||
class Test4ClassVariables1
|
||||
@@var1 = 1
|
||||
end
|
||||
class Test4ClassVariables2 < Test4ClassVariables1
|
||||
@@var2 = 2
|
||||
end
|
||||
|
||||
Test4ClassVariables1.class_variables == [:@@var1] &&
|
||||
Test4ClassVariables2.class_variables == [:@@var2]
|
||||
end
|
||||
|
||||
assert('Module#const_defined?', '15.2.2.4.20') do
|
||||
module Test4ConstDefined
|
||||
Const4Test4ConstDefined = true
|
||||
|
||||
@@ -61,6 +61,32 @@ assert('String#[]', '15.2.10.5.6') do
|
||||
a3 == 'bc' and b3 == nil
|
||||
end
|
||||
|
||||
assert('String#[] with Range') do
|
||||
a1 = 'abc'[1..0]
|
||||
b1 = 'abc'[1..1]
|
||||
c1 = 'abc'[1..2]
|
||||
d1 = 'abc'[1..3]
|
||||
e1 = 'abc'[1..4]
|
||||
f1 = 'abc'[0..-2]
|
||||
g1 = 'abc'[-2..3]
|
||||
h1 = 'abc'[3..4]
|
||||
i1 = 'abc'[4..5]
|
||||
a2 = 'abc'[1...0]
|
||||
b2 = 'abc'[1...1]
|
||||
c2 = 'abc'[1...2]
|
||||
d2 = 'abc'[1...3]
|
||||
e2 = 'abc'[1...4]
|
||||
f2 = 'abc'[0...-2]
|
||||
g2 = 'abc'[-2...3]
|
||||
h2 = 'abc'[3...4]
|
||||
i2 = 'abc'[4...5]
|
||||
|
||||
a1 == '' and b1 == 'b' and c1 == 'bc' and d1 == 'bc' and e1 == 'bc' and
|
||||
f1 == 'ab' and g1 == 'bc' and h1 == '' and i2 == nil and
|
||||
a2 == '' and b2 == '' and c2 == 'b' and d2 == 'bc' and e2 == 'bc' and
|
||||
f2 == 'a' and g2 == 'bc' and h2 == '' and i2 == nil
|
||||
end
|
||||
|
||||
assert('String#capitalize', '15.2.10.5.7') do
|
||||
a = 'abc'
|
||||
a.capitalize
|
||||
|
||||
Reference in New Issue
Block a user