Skip to content

Commit cd42dc1

Browse files
authored
Merge pull request jruby#8533 from headius/spec_fixes
Fixes and impls for Ruby 3.4 specs
2 parents 4e925df + 0c6a040 commit cd42dc1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+816
-309
lines changed

core/src/main/java/org/jruby/Ruby.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,9 @@ private Ruby(RubyInstanceConfig config) {
401401
// Initialize all the core classes
402402
comparableModule = RubyComparable.createComparable(context);
403403
enumerableModule = RubyEnumerable.createEnumerableModule(context);
404+
404405
stringClass = RubyString.createStringClass(context, objectClass, comparableModule);
406+
emptyFrozenString = freezeAndDedupString(newEmptyString(context));
405407

406408
falseString = Create.newString(context, FALSE_BYTES);
407409
falseString.setFrozen(true);
@@ -3528,6 +3530,10 @@ public RubyArray getEmptyFrozenArray() {
35283530
return emptyFrozenArray;
35293531
}
35303532

3533+
public RubyString getEmptyFrozenString() {
3534+
return emptyFrozenString;
3535+
}
3536+
35313537
public RubyBoolean newBoolean(boolean value) {
35323538
return value ? trueObject : falseObject;
35333539
}
@@ -4448,7 +4454,7 @@ public RaiseException newStopIteration(IRubyObject result, String message) {
44484454
RubyException ex = RubyStopIteration.newInstance(context, result, message);
44494455

44504456
if (!RubyInstanceConfig.STOPITERATION_BACKTRACE) {
4451-
ex.setBacktrace(disabledBacktrace());
4457+
ex.setBacktrace(context, disabledBacktrace());
44524458
}
44534459

44544460
return ex.toThrowable();
@@ -5207,7 +5213,7 @@ public interface RecursiveFunctionEx<T> extends ThreadContext.RecursiveFunctionE
52075213
public interface RecursiveFunction extends MRIRecursionGuard.RecursiveFunction {}
52085214

52095215
/**
5210-
* @deprecated Use ThreadContext.safeRecurse
5216+
* @deprecated Use {@link ThreadContext#safeRecurse(ThreadContext.RecursiveFunctionEx, Object, IRubyObject, String, boolean)}
52115217
*/
52125218
@Deprecated
52135219
public <T> IRubyObject safeRecurse(RecursiveFunctionEx<T> func, ThreadContext context, T state, IRubyObject obj, String name, boolean outer) {
@@ -5915,6 +5921,7 @@ public int applyAsInt(ThreadContext context) {
59155921
}
59165922

59175923
private final RubyArray emptyFrozenArray;
5924+
private final RubyString emptyFrozenString;
59185925

59195926
/**
59205927
* A map from Ruby string data to a pre-frozen global version of that string.

core/src/main/java/org/jruby/RubyArray.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,6 +1402,13 @@ public IRubyObject subseq_step(ThreadContext context, RubyArithmeticSequence arg
14021402
len = realLength;
14031403

14041404
if (step < 0) {
1405+
if (aseqExcl && !aseqEnd.isNil()) {
1406+
/* Handle exclusion before range reversal */
1407+
aseqEnd = asFixnum(context, aseqEnd.convertToInteger().getIntValue() + 1);
1408+
1409+
/* Don't exclude the previous beginning */
1410+
aseqExcl = false;
1411+
}
14051412
IRubyObject tmp = aseqBeg;
14061413
aseqBeg = aseqEnd;
14071414
aseqEnd = tmp;

core/src/main/java/org/jruby/RubyBasicObject.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2207,7 +2207,7 @@ public IRubyObject display(ThreadContext context, IRubyObject[] args) {
22072207
*
22082208
* <em>produces:</em>
22092209
*
2210-
* prog.rb:3:in `&lt;&lt;': can't modify frozen array (TypeError)
2210+
* prog.rb:3:in '&lt;&lt;': can't modify frozen array (TypeError)
22112211
* from prog.rb:3
22122212
*/
22132213
public IRubyObject freeze(ThreadContext context) {

core/src/main/java/org/jruby/RubyBignum.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public static long big2long(RubyBignum val) {
198198
BigInteger big = val.value;
199199

200200
if (big.compareTo(LONG_MIN) < 0 || big.compareTo(LONG_MAX) > 0) {
201-
throw val.getRuntime().newRangeError("bignum too big to convert into `long'");
201+
throw val.getRuntime().newRangeError("bignum too big to convert into 'long'");
202202
}
203203
return big.longValue();
204204
}
@@ -216,7 +216,7 @@ public static long big2ulong(RubyBignum value) {
216216

217217
public static long big2ulong(Ruby runtime, BigInteger big) {
218218
if (big.compareTo(BigInteger.ZERO) < 0 || big.compareTo(ULONG_MAX) > 0) {
219-
throw runtime.newRangeError("bignum out of range for `ulong'");
219+
throw runtime.newRangeError("bignum out of range for 'ulong'");
220220
}
221221

222222
return big.longValue();
@@ -706,9 +706,8 @@ public IRubyObject op_pow(ThreadContext context, IRubyObject other) {
706706
RubyComplex complex = RubyComplex.newComplexRaw(context.runtime, this);
707707
return sites(context).op_exp.call(context, complex, complex, other);
708708
}
709-
} else if (other instanceof RubyBignum bignum) {
710-
d = bignum.getDoubleValue();
711-
warn(context, "in a**b, b may be too big");
709+
} else if (other instanceof RubyBignum) {
710+
throw argumentError(context, "exponent is too large");
712711
} else if (other instanceof RubyFixnum fixnum) {
713712
return op_pow(context, fixnum.value);
714713
} else {
@@ -737,8 +736,7 @@ public final IRubyObject op_pow(final ThreadContext context, final long other) {
737736
}
738737
final int xbits = value.bitLength();
739738
if ((xbits > BIGLEN_LIMIT) || (xbits * other > BIGLEN_LIMIT)) {
740-
warn(context, "in a**b, b may be too big");
741-
return pow(context, (double) other);
739+
throw argumentError(context, "exponent is too large");
742740
} else {
743741
return newBignum(context.runtime, value.pow((int) other));
744742
}

core/src/main/java/org/jruby/RubyClass.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,7 @@ private RubyClass initializeCommon(ThreadContext context, RubyClass superClazz,
10821082
@JRubyMethod(name = "initialize_copy", visibility = PRIVATE)
10831083
public IRubyObject initialize_copy(ThreadContext context, IRubyObject original) {
10841084
checkNotInitialized(context);
1085+
if (original == context.runtime.getBasicObject()) throw typeError(context, "can't copy the root class");
10851086
if (original instanceof MetaClass) throw typeError(context, "can't copy singleton class");
10861087

10871088
super.initialize_copy(context, original);
@@ -2914,7 +2915,7 @@ public IRubyObject smartLoadNewUser(IRubyObject target, IRubyObject data) {
29142915
target.callMethod(context, "marshal_load", data);
29152916
return target;
29162917
} else {
2917-
throw typeError(context, "class ", this, " needs to have method `marshal_load'");
2918+
throw typeError(context, "class ", this, " needs to have method 'marshal_load'");
29182919
}
29192920

29202921
} else if (!(cache = searchWithCache("marshal_load")).method.isUndefined()) {
@@ -2964,7 +2965,7 @@ public IRubyObject smartLoadOldUser(IRubyObject data) {
29642965
if (method.call(context, this, cache.sourceModule, "respond_to?", asSymbol(context, "_load")).isTrue()) {
29652966
return callMethod(context, "_load", data);
29662967
} else {
2967-
throw typeError(context, "class ", this, " needs to have method `_load'");
2968+
throw typeError(context, "class ", this, " needs to have method '_load'");
29682969
}
29692970
} else if (!(cache = singleton.searchWithCache("_load")).method.isUndefined()) {
29702971
// real _load defined, cache and call it
@@ -2973,7 +2974,7 @@ public IRubyObject smartLoadOldUser(IRubyObject data) {
29732974

29742975
} else {
29752976
// provide an error, since it doesn't exist
2976-
throw typeError(context, "class ", this, " needs to have method `_load'");
2977+
throw typeError(context, "class ", this, " needs to have method '_load'");
29772978
}
29782979
}
29792980

core/src/main/java/org/jruby/RubyComplex.java

Lines changed: 105 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.jcodings.specific.ASCIIEncoding;
3030
import org.jruby.anno.JRubyClass;
3131
import org.jruby.anno.JRubyMethod;
32+
import org.jruby.api.Convert;
3233
import org.jruby.ast.util.ArgsUtil;
3334
import org.jruby.exceptions.RaiseException;
3435
import org.jruby.runtime.Arity;
@@ -54,7 +55,9 @@
5455

5556
import static org.jruby.api.Convert.asBoolean;
5657
import static org.jruby.api.Convert.asFixnum;
58+
import static org.jruby.api.Convert.asFloat;
5759
import static org.jruby.api.Create.newArray;
60+
import static org.jruby.api.Create.newRational;
5861
import static org.jruby.api.Define.defineClass;
5962
import static org.jruby.api.Error.*;
6063
import static org.jruby.api.Warn.warn;
@@ -494,16 +497,16 @@ private static IRubyObject convertCommon(ThreadContext context, IRubyObject recv
494497

495498
if (a1 instanceof RubyComplex) {
496499
RubyComplex a1c = (RubyComplex) a1;
497-
if (k_exact_p(a1c.image) && f_zero_p(context, a1c.image)) a1 = a1c.real;
500+
if (k_exact_zero_p(context, a1c.image)) a1 = a1c.real;
498501
}
499502

500503
if (a2 instanceof RubyComplex) {
501504
RubyComplex a2c = (RubyComplex) a2;
502-
if (k_exact_p(a2c.image) && f_zero_p(context, a2c.image)) a2 = a2c.real;
505+
if (k_exact_zero_p(context, a2c.image)) a2 = a2c.real;
503506
}
504507

505508
if (a1 instanceof RubyComplex) {
506-
if (singleArg || (k_exact_p(a2) && f_zero_p(context, a2))) return a1;
509+
if (singleArg || (k_exact_zero_p(context, a2))) return a1;
507510
}
508511

509512
if (singleArg) {
@@ -706,23 +709,30 @@ public IRubyObject fdiv(ThreadContext context, IRubyObject other) {
706709
return f_divide(context, this, other, (a, b) -> fdiv.call(context, a, a, b), fdiv);
707710
}
708711

709-
/** nucomp_expt
710-
*
712+
/**
713+
* MRI: nucomp_expt
711714
*/
712715
@JRubyMethod(name = "**")
713716
public IRubyObject op_expt(ThreadContext context, IRubyObject other) {
714-
if (k_exact_p(other) && f_zero_p(context, other)) {
715-
return newComplexBang(context, getMetaClass(), asFixnum(context, 0));
717+
if (other instanceof RubyNumeric && k_exact_zero_p(context, other)) {
718+
return newComplexBang(context, getMetaClass(), asFixnum(context, 1));
716719
}
717720

718-
if (other instanceof RubyRational && f_one_p(context, f_denominator(context, other))) {
721+
if (other instanceof RubyRational otherRational && f_one_p(context, otherRational.getDenominator())) {
719722
other = f_numerator(context, other);
720723
}
721724

722-
if (other instanceof RubyComplex otherComplex && f_zero_p(context, otherComplex.image)) {
725+
if (other instanceof RubyComplex otherComplex && k_exact_zero_p(context, otherComplex.image)) {
723726
other = otherComplex.real;
724727
}
725728

729+
if (other == RubyFixnum.one(context.runtime)) {
730+
return newComplex(context, metaClass, real, image);
731+
}
732+
733+
IRubyObject result = complexPowForSpecialAngle(context, other);
734+
if (result != UNDEF) return result;
735+
726736
if (other instanceof RubyComplex otherComplex) {
727737
IRubyObject otherReal = otherComplex.real;
728738
IRubyObject otherImage = otherComplex.image;
@@ -807,6 +817,82 @@ else if (f_zero_p(context, xr)) {
807817
return coerceBin(context, sites(context).op_exp, other);
808818
}
809819

820+
// MRI: complex_pow_for_special_angle
821+
private IRubyObject complexPowForSpecialAngle(ThreadContext context, IRubyObject other) {
822+
if (!(other instanceof RubyInteger integer)) {
823+
return UNDEF;
824+
}
825+
826+
IRubyObject x = UNDEF;
827+
int dir;
828+
if (f_zero_p(context, image)) {
829+
x = real;
830+
dir = 0;
831+
}
832+
else if (f_zero_p(context, real)) {
833+
x = image;
834+
dir = 2;
835+
}
836+
else if (Numeric.f_eqeq_p(context, real, image)) {
837+
x = real;
838+
dir = 1;
839+
}
840+
else if (Numeric.f_eqeq_p(context, real, Numeric.f_negate(context, image))) {
841+
x = image;
842+
dir = 3;
843+
} else {
844+
dir = 0;
845+
}
846+
847+
if (x == UNDEF) return x;
848+
849+
if (f_negative_p(context, x)) {
850+
x = f_negate(context, x);
851+
dir += 4;
852+
}
853+
854+
IRubyObject zx;
855+
if (dir % 2 == 0) {
856+
zx = num_pow(context, x, other);
857+
}
858+
else {
859+
RubyFixnum two = RubyFixnum.two(context.runtime);
860+
zx = num_pow(context,
861+
sites(context).op_times.call(context, two.op_mul(context, x), x),
862+
integer.div(context, two)
863+
);
864+
if (f_odd_p(context, other)) {
865+
zx = sites(context).op_times.call(context, zx, x);
866+
}
867+
}
868+
int dirs[][] = {
869+
{1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1}
870+
};
871+
int z_dir = fix2int(asFixnum(context, dir).modulo(context, 8));
872+
873+
IRubyObject zr = context.fals, zi = context.fals;
874+
switch (dirs[z_dir][0]) {
875+
case 0: zr = zero_for(context, zx); break;
876+
case 1: zr = zx; break;
877+
case -1: zr = f_negate(context, zx); break;
878+
}
879+
switch (dirs[z_dir][1]) {
880+
case 0: zi = zero_for(context, zx); break;
881+
case 1: zi = zx; break;
882+
case -1: zi = f_negate(context, zx); break;
883+
}
884+
return newComplex(context, metaClass, zr, zi);
885+
}
886+
887+
private static IRubyObject zero_for(ThreadContext context, IRubyObject x) {
888+
if (x instanceof RubyFloat)
889+
return asFloat(context, 0);
890+
if (x instanceof RubyRational)
891+
return newRational(context, 0, 1);
892+
893+
return asFixnum(context, 0);
894+
}
895+
810896
/** nucomp_equal_p
811897
*
812898
*/
@@ -1131,7 +1217,15 @@ public IRubyObject to_f(ThreadContext context) {
11311217
*/
11321218
@JRubyMethod(name = "to_r")
11331219
public IRubyObject to_r(ThreadContext context) {
1134-
checkValidRational(context, "Rational");
1220+
if (image instanceof RubyFloat imageFloat && imageFloat.isZero(context)) {
1221+
/* Do nothing here */
1222+
} else if (!k_exact_zero_p(context, image)) {
1223+
IRubyObject imag = Convert.checkToRational(context, image);
1224+
if (imag.isNil() || !k_exact_zero_p(context, imag)) {
1225+
throw rangeError(context, "can't convert " + this + " into Rational");
1226+
}
1227+
}
1228+
11351229
return f_to_r(context, real);
11361230
}
11371231

@@ -1145,7 +1239,7 @@ public IRubyObject rationalize(ThreadContext context, IRubyObject[] args) {
11451239
}
11461240

11471241
private void checkValidRational(ThreadContext context, String type) {
1148-
if (k_inexact_p(image) || !f_zero_p(context, image)) {
1242+
if (!k_exact_zero_p(context, image)) {
11491243
throw rangeError(context, "can't convert " + f_to_s(context, this).convertToString() + " into " + type);
11501244
}
11511245
}

core/src/main/java/org/jruby/RubyDir.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
import static org.jruby.api.Create.newString;
7777
import static org.jruby.api.Define.defineClass;
7878
import static org.jruby.api.Error.argumentError;
79+
import static org.jruby.api.Error.notImplementedError;
7980
import static org.jruby.api.Error.runtimeError;
8081
import static org.jruby.api.Warn.warn;
8182
import static org.jruby.util.RubyStringBuilder.str;
@@ -483,6 +484,11 @@ private static IRubyObject chdirCommon(ThreadContext context, Block block, RubyS
483484
return result;
484485
}
485486

487+
@JRubyMethod(name = "fchdir", meta = true, notImplemented = true)
488+
public static IRubyObject fchdir(ThreadContext context, IRubyObject dirClass, IRubyObject ignored) {
489+
throw notImplementedError(context, "Dir.fchdir");
490+
}
491+
486492
@JRubyMethod(name = "chdir")
487493
public IRubyObject chdir(ThreadContext context) {
488494
return chdir(context, this.getMetaClass(), path, Block.NULL_BLOCK);

core/src/main/java/org/jruby/RubyEncoding.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
import org.jcodings.Encoding;
3939
import org.jcodings.EncodingDB.Entry;
40+
import org.jcodings.specific.ASCIIEncoding;
4041
import org.jcodings.specific.USASCIIEncoding;
4142
import org.jcodings.specific.UTF16BEEncoding;
4243
import org.jcodings.specific.UTF8Encoding;
@@ -72,6 +73,7 @@ public class RubyEncoding extends RubyObject implements Constantizable {
7273
public static final ByteList EXTERNAL = new ByteList(encodeISO("external"), false);
7374
public static final ByteList FILESYSTEM = new ByteList(encodeISO("filesystem"), false);
7475
public static final ByteList INTERNAL = new ByteList(encodeISO("internal"), false);
76+
public static final ByteList BINARY_ASCII_NAME = new ByteList(encodeISO("BINARY (ASCII-8BIT)"), false);
7577

7678
public static RubyClass createEncodingClass(ThreadContext context, RubyClass Object) {
7779
return defineClass(context, "Encoding", Object, NOT_ALLOCATABLE_ALLOCATOR).
@@ -574,12 +576,19 @@ public IRubyObject to_s(ThreadContext context) {
574576
public IRubyObject inspect(ThreadContext context) {
575577
ByteList bytes = new ByteList();
576578
bytes.append("#<Encoding:".getBytes());
577-
bytes.append(name);
579+
bytes.append(inspectName());
578580
if (isDummy) bytes.append(" (dummy)".getBytes());
579581
bytes.append('>');
580582
return RubyString.newUsAsciiStringNoCopy(context.runtime, bytes);
581583
}
582584

585+
private ByteList inspectName() {
586+
if (encoding == ASCIIEncoding.INSTANCE) {
587+
return BINARY_ASCII_NAME;
588+
}
589+
return name;
590+
}
591+
583592
@SuppressWarnings("unchecked")
584593
@JRubyMethod(name = "names")
585594
public IRubyObject names(ThreadContext context) {

0 commit comments

Comments
 (0)