Skip to content

Commit

Permalink
Merge branch 'master' into bytelist_love
Browse files Browse the repository at this point in the history
  • Loading branch information
enebo committed Apr 25, 2018
2 parents e1f6790 + 054235f commit 52a1832
Show file tree
Hide file tree
Showing 13 changed files with 123 additions and 52 deletions.
50 changes: 34 additions & 16 deletions core/src/main/java/org/jruby/RubyArray.java
Expand Up @@ -1912,10 +1912,10 @@ public IRubyObject join(ThreadContext context) {
return join19(context);
}

// 1.9 MRI: ary_join_0
// MRI: ary_join_0
protected RubyString joinStrings(RubyString sep, int max, RubyString result) {
IRubyObject first = eltOk(0);
if (max > 0 && first instanceof EncodingCapable) {
IRubyObject first;
if (max > 0 && (first = eltOk(0)) instanceof EncodingCapable) {
result.setEncoding(((EncodingCapable) first).getEncoding());
}

Expand All @@ -1931,8 +1931,8 @@ protected RubyString joinStrings(RubyString sep, int max, RubyString result) {
return result;
}

// 1.9 MRI: ary_join_1
private RubyString joinAny(ThreadContext context, RubyString sep, int i, RubyString result) {
// MRI: ary_join_1
private RubyString joinAny(ThreadContext context, RubyString sep, int i, RubyString result, boolean[] first) {
assert i >= 0 : "joining elements before beginning of array";

RubyClass arrayClass = context.runtime.getArray();
Expand All @@ -1944,36 +1944,48 @@ private RubyString joinAny(ThreadContext context, RubyString sep, int i, RubyStr
IRubyObject val = eltOk(i);

if (val instanceof RubyString) {
result.append19(val);
strJoin(result, val, first);
} else if (val instanceof RubyArray) {
recursiveJoin(context, val, sep, result, (RubyArray) val);
recursiveJoin(context, val, sep, result, (RubyArray) val, first);
} else {
IRubyObject tmp = val.checkStringType();
if (tmp != context.nil) {
result.append19(tmp);
strJoin(result, tmp, first);
continue;
}

if (to_ary_checked == null) to_ary_checked = sites(context).to_ary_checked;

tmp = TypeConverter.convertToTypeWithCheck(context, val, arrayClass, to_ary_checked);
if (tmp != context.nil) {
recursiveJoin(context, val, sep, result, (RubyArray) tmp);
recursiveJoin(context, val, sep, result, (RubyArray) tmp, first);
} else {
result.append19(RubyString.objAsString(context, val));
val = RubyString.objAsString(context, val);
strJoin(result, val, first);
}
}
}

return result;
}

// MRI: ary_join_1, str_join label
private void strJoin(RubyString result, IRubyObject tmp, boolean[] first) {
result.append19(tmp);
if (first[0]) {
result.setEncoding(((RubyString) tmp).getEncoding());
first[0] = false;
}
}

private void recursiveJoin(final ThreadContext context, final IRubyObject outValue,
final RubyString sep, final RubyString result, final RubyArray ary) {
final RubyString sep, final RubyString result, final RubyArray ary, final boolean[] first) {

if (ary == this) throw context.runtime.newArgumentError("recursive array join");

context.safeRecurse(JOIN_RECURSIVE, new JoinRecursive.State(ary, outValue, sep, result), outValue, "join", true);
first[0] = false;

context.safeRecurse(JOIN_RECURSIVE, new JoinRecursive.State(ary, outValue, sep, result, first), outValue, "join", true);
}

/** rb_ary_join
Expand All @@ -1994,14 +2006,18 @@ public IRubyObject join19(final ThreadContext context, IRubyObject sep) {
len += sepString.size() * (realLength - 1);
}

boolean[] first = null;
for (int i = 0; i < realLength; i++) {
IRubyObject val = eltOk(i);
IRubyObject tmp = val.checkStringType();
if (tmp == context.nil || tmp != val) {
if (first == null) first = new boolean[] {false};
else first[0] = false;
len += (realLength - i) * 10;
RubyString result = (RubyString) RubyString.newStringLight(runtime, len, USASCIIEncoding.INSTANCE).infectBy(this);

return joinAny(context, sepString, i, joinStrings(sepString, i, result));
joinStrings(sepString, i, result);
first[0] = i == 0;
return joinAny(context, sepString, i, result, first);
}

len += ((RubyString) tmp).getByteList().length();
Expand Down Expand Up @@ -5139,19 +5155,21 @@ protected static class State {
//private final IRubyObject outValue;
private final RubyString sep;
private final RubyString result;
private final boolean[] first;

State(RubyArray ary, IRubyObject outValue, RubyString sep, RubyString result) {
State(RubyArray ary, IRubyObject outValue, RubyString sep, RubyString result, boolean[] first) {
this.ary = ary;
//this.outValue = outValue;
this.sep = sep;
this.result = result;
this.first = first;
}
}

public IRubyObject call(ThreadContext context, State state, IRubyObject obj, boolean recur) {
if (recur) throw context.runtime.newArgumentError("recursive array join");

state.ary.joinAny(context, state.sep, 0, state.result);
state.ary.joinAny(context, state.sep, 0, state.result, state.first);

return context.nil;
}
Expand Down
23 changes: 1 addition & 22 deletions core/src/main/java/org/jruby/RubyBignum.java
Expand Up @@ -1234,32 +1234,11 @@ public RubyRational convertToRational() {
public IRubyObject sqrt(ThreadContext context) {
Ruby runtime = context.runtime;

// TODO: this not exactly the same as MRI

if (isNegative()) {
throw runtime.newMathDomainError("Numerical argument is out of domain - isqrt");
}
return bignorm(runtime, sqrt(value));
}

/**
* A simple public-domain iterative BigInteger sqrt.
*
* @param x
* @return
*/
public static BigInteger sqrt(BigInteger x) {
BigInteger div = BigInteger.ZERO.setBit(x.bitLength()/2);
BigInteger div2 = div;
// Loop until we hit the same value twice in a row, or wind
// up alternating.
for(;;) {
BigInteger y = div.add(x.divide(div)).shiftRight(1);
if (y.equals(div) || y.equals(div2))
return y;
div2 = div;
div = y;
}
return bignorm(runtime, floorSqrt(value));
}

private static JavaSites.BignumSites sites(ThreadContext context) {
Expand Down
7 changes: 4 additions & 3 deletions core/src/main/java/org/jruby/RubyFixnum.java
Expand Up @@ -1472,13 +1472,14 @@ public IRubyObject remainder(ThreadContext context, IRubyObject y) {
public IRubyObject sqrt(ThreadContext context) {
Ruby runtime = context.runtime;

// TODO: this not exactly the same as MRI

if (isNegative()) {
throw runtime.newMathDomainError("Numerical argument is out of domain - isqrt");
}

long n = value;
long sq = (long) Math.sqrt(n);
// FIXME: this is obviously not super efficient, but floorSqrt(long) did not pass tests
long sq = floorSqrt(BigInteger.valueOf(n)).longValue();

return RubyFixnum.newFixnum(runtime, sq);
}

Expand Down
32 changes: 32 additions & 0 deletions core/src/main/java/org/jruby/RubyInteger.java
Expand Up @@ -150,6 +150,38 @@ public static IRubyObject sqrt(ThreadContext context, IRubyObject self, IRubyObj

public abstract IRubyObject sqrt(ThreadContext context);

// floorSqrt :: unsigned long -> unsigned int
// Gives the exact floor of the square root of x, treated as unsigned.
// Public domain code from http://www.codecodex.com/wiki/Calculate_an_integer_square_root
public static final int floorSqrt(final long x) {
if ((x & 0xfff0000000000000L) == 0L) return (int) StrictMath.sqrt(x);
final long result = (long) StrictMath.sqrt(2.0d*(x >>> 1));
return result*result - x > 0L ? (int) result - 1 : (int) result;
}

// floorSqrt :: BigInteger -> BigInteger
// Gives the exact floor of the square root of x, returning null (like Math.sqrt's NaN) if x is negative.
// // Public domain code from http://www.codecodex.com/wiki/Calculate_an_integer_square_root
public static final BigInteger floorSqrt(final BigInteger x) {
if (x == null) return null;

final int zeroCompare = x.compareTo(BigInteger.ZERO);
if (zeroCompare < 0) return null;
if (zeroCompare == 0) return BigInteger.ZERO;

int bit = Math.max(0, (x.bitLength() - 63) & 0xfffffffe); // last even numbered bit in first 64 bits
BigInteger result = BigInteger.valueOf(floorSqrt(x.shiftRight(bit).longValue()) & 0xffffffffL);
bit >>>= 1;
result = result.shiftLeft(bit);
while (bit != 0) {
bit--;
final BigInteger resultHigh = result.setBit(bit);
if (resultHigh.multiply(resultHigh).compareTo(x) <= 0) result = resultHigh;
}

return result;
}

/* ================
* Instance Methods
* ================
Expand Down
6 changes: 4 additions & 2 deletions core/src/main/java/org/jruby/embed/jsr223/JRubyEngine.java
Expand Up @@ -214,7 +214,8 @@ public Object invokeMethod(Object receiver, String method, Object... args)
}
return container.callMethod(receiver, method, args, Object.class);
} catch (Exception e) {
if (e.getCause().getMessage().contains("undefined method")) {
if (e.getCause() != null && e.getCause().getMessage() != null
&& e.getCause().getMessage().contains("undefined method")) {
throw wrapMethodException(e);
}
throw wrapException(e);
Expand All @@ -239,7 +240,8 @@ public Object invokeFunction(String method, Object... args)
}
return container.callMethod(container.getProvider().getRuntime().getTopSelf(), method, args, Object.class);
} catch (Exception e) {
if (e.getCause().getMessage().contains("undefined method")) {
if (e.getCause() != null && e.getCause().getMessage() != null
&& e.getCause().getMessage().contains("undefined method")) {
throw wrapMethodException(e);
}
throw wrapException(e);
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/util/StringSupport.java
Expand Up @@ -1855,7 +1855,7 @@ public static IRubyObject rbStrEnumerateLines(RubyString str, ThreadContext cont
if (EncodingUtils.encAscget(strBytes, subend, pend, n, enc) != '\r') {
n[0] = 0;
}
rslen = n[0] + enc.length(strBytes, subend + n[0], pend);
rslen = n[0] + length(enc, strBytes, subend + n[0], pend);
if (enc.isNewLine(strBytes, subend + n[0], pend)) {
if (eol == subend) break;
subend += rslen;
Expand Down
15 changes: 9 additions & 6 deletions core/src/main/java/org/jruby/util/io/EncodingUtils.java
Expand Up @@ -1518,7 +1518,7 @@ static void moreOutputBuffer(ByteList destination, ResizeFunction resizeDestinat
outStop.p = outStart.p + newLen;
}

// io_set_encoding_by_bom
// MRI: io_set_encoding_by_bom
public static void ioSetEncodingByBOM(ThreadContext context, RubyIO io) {
Ruby runtime = context.runtime;
Encoding bomEncoding = ioStripBOM(context, io);
Expand All @@ -1534,15 +1534,13 @@ public static void ioSetEncodingByBOM(ThreadContext context, RubyIO io) {
}
}

// mri: io_strip_bom
@Deprecated
public static Encoding ioStripBOM(RubyIO io) {
return ioStripBOM(io.getRuntime().getCurrentContext(), io);
}
// MRI: io_strip_bom
public static Encoding ioStripBOM(ThreadContext context, RubyIO io) {
IRubyObject b1, b2, b3, b4;

if ((io.getOpenFile().getMode() & OpenFile.READABLE) == 0) return null;
if ((b1 = io.getbyte(context)).isNil()) return null;

switch ((int)((RubyFixnum)b1).getLongValue()) {
case 0xEF:
if ((b2 = io.getbyte(context)).isNil()) break;
Expand Down Expand Up @@ -2280,4 +2278,9 @@ public static int encCodelen(ThreadContext context, int c, Encoding enc) {
return n;
}

@Deprecated
public static Encoding ioStripBOM(RubyIO io) {
return ioStripBOM(io.getRuntime().getCurrentContext(), io);
}

}
30 changes: 30 additions & 0 deletions core/src/test/java/org/jruby/embed/jsr223/JRubyEngineTest.java
Expand Up @@ -617,6 +617,21 @@ public void testInvokeMethod() throws Exception {
instance.getBindings(ScriptContext.ENGINE_SCOPE).clear();
}

/**
* Test of invokeMethod method throwing an exception with a null message.
*/
@Test
public void testInvokeMethodWithJavaException() throws Exception {
ScriptEngine instance = newScriptEngine();
instance.eval("def trigger_npe\nraise java.lang.NullPointerException.new\nend");
try {
Object result = ((Invocable) instance).invokeMethod(Object.class,"trigger_npe", null);
fail("Expected javax.script.ScriptException");
} catch (javax.script.ScriptException sex) {
// javax.script.ScriptException is expected
}
}

/**
* Test of invokeFunction method, of class Jsr223JRubyEngine.
*/
Expand All @@ -643,6 +658,21 @@ public void testInvokeFunction() throws Exception {
instance.getBindings(ScriptContext.ENGINE_SCOPE).clear();
}

/**
* Test of invokeFunction method throwing an exception with a null message.
*/
@Test
public void testInvokeFunctionWithJavaException() throws Exception {
ScriptEngine instance = newScriptEngine();
instance.eval("def trigger_npe\nraise java.lang.NullPointerException.new\nend");
try {
Object result = ((Invocable) instance).invokeFunction("trigger_npe", null);
fail("Expected javax.script.ScriptException");
} catch (javax.script.ScriptException sex) {
// javax.script.ScriptException is expected
}
}

/**
* Test of getInterface method, of class Jsr223JRubyEngine.
*/
Expand Down
3 changes: 2 additions & 1 deletion lib/ruby/stdlib/io/nonblock.rb
Expand Up @@ -9,12 +9,13 @@ def nonblock=(nonblocking)
end

def nonblock(nonblocking = true)
old_blocking = JRuby.reference(self).blocking?
JRuby.reference(self).blocking = !nonblocking;
if block_given?
begin
yield self
ensure
JRuby.reference(self).blocking = nonblocking;
JRuby.reference(self).blocking = old_blocking;
end
end
end
Expand Down
1 change: 1 addition & 0 deletions test/mri/excludes/TestCoverage.rb
Expand Up @@ -2,6 +2,7 @@
exclude :test_branch_coverage_for_if_statement, "no support for coverage options yet (#5147)"
exclude :test_branch_coverage_for_safe_method_invocation, "no support for coverage options yet (#5147)"
exclude :test_branch_coverage_for_while_statement, "no support for coverage options yet (#5147)"
exclude :test_eval, "line number is not matching in eval"
exclude :test_line_coverage_for_multiple_lines, "no support for coverage options yet (#5147)"
exclude :test_method_coverage, "no support for coverage options yet (#5147)"
exclude :test_method_coverage_for_alias, "no support for coverage options yet (#5147)"
Expand Down
1 change: 1 addition & 0 deletions test/mri/excludes/TestLazyEnumerator.rb
@@ -1,3 +1,4 @@
exclude :test_inspect, "needs investigation"
exclude :test_laziness_conservation, "needs investigation"
exclude :test_map_packed_nested, "new logic for preserving packed status of args (2.5)"
exclude :test_size, "needs investigation"
2 changes: 2 additions & 0 deletions test/mri/excludes/TestMethod.rb
@@ -1,4 +1,5 @@
exclude :test___dir__, "needs investigation"
exclude :test_argument_error_location, "argument errors are calculated differently in JRuby"
exclude :test_body, "fails due RubyVM constant"
exclude :test_callee, "needs investigation"
exclude :test_define_method_visibility, "needs investigation"
Expand All @@ -9,4 +10,5 @@
exclude :test_prepended_public_zsuper, "2.4 fix/change to prepend + method + super_method (#4687)"
exclude :test_splat_long_array, "passes locally but fails on travis OOME"
exclude :test_super_method_removed, "finds super method when super method has been undef (#2155)"
exclude :test_super_method_with_prepended_module, "2.5 test for super with prepend has issues (#4687)"
exclude :test_to_proc_binding, "NullPointerException in parser"
3 changes: 2 additions & 1 deletion test/mri/excludes/TestNumeric.rb
@@ -1 +1,2 @@
exclude :test_coerce, "Needs UTF8 symbol support"
exclude :test_coerce, "Needs UTF8 symbol support"
exclude :test_step, "Buggy test, see https://bugs.ruby-lang.org/issues/14712"

0 comments on commit 52a1832

Please sign in to comment.