Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2419fa0ada33
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: bc08d1dfcc5d
Choose a head ref
  • 6 commits
  • 9 files changed
  • 1 contributor

Commits on Feb 7, 2018

  1. Copy the full SHA
    de6c8bd View commit details

Commits on Feb 8, 2018

  1. [refactor] improve RubyHash sub-classes used for globals e.g. ENV

    override internalGetEntry so all retrieval operations work as expected
    kares committed Feb 8, 2018
    Copy the full SHA
    546aaef View commit details
  2. Copy the full SHA
    deaeea6 View commit details
  3. also override interface pre-scribed hash() method on RubyArray

    ... note on 'missing' explicit hashCode (despite most core types have)
    kares committed Feb 8, 2018
    Copy the full SHA
    ab2569c View commit details
  4. Copy the full SHA
    666ffb9 View commit details
  5. Copy the full SHA
    bc08d1d View commit details
22 changes: 17 additions & 5 deletions core/src/main/java/org/jruby/RubyArray.java
Original file line number Diff line number Diff line change
@@ -771,14 +771,21 @@ public RubyFixnum hash19(ThreadContext context) {
return hash(context);
}

@Override
public RubyFixnum hash() {
return hash(getRuntime().getCurrentContext());
}

/** rb_ary_hash
*
*/
@JRubyMethod(name = "hash")
public RubyFixnum hash(ThreadContext context) {
Ruby runtime = context.runtime;
return RubyFixnum.newFixnum(context.runtime, hashImpl(context));
}

long h = Helpers.hashStart(runtime, realLength);
private long hashImpl(final ThreadContext context) {
long h = Helpers.hashStart(context.runtime, realLength);

h = Helpers.murmurCombine(h, System.identityHashCode(RubyArray.class));

@@ -788,11 +795,16 @@ public RubyFixnum hash(ThreadContext context) {
h = murmurCombine(h, n.getLongValue());
}

h = hashEnd(h);

return runtime.newFixnum(h);
return hashEnd(h);
}

// NOTE: there's some (passing) RubySpec where [ ary ] is mocked with a custom hash
// maybe JRuby doesn't need to obey 100% since it already has hashCode on other core types
//@Override
//public int hashCode() {
// return (int) hashImpl(getRuntime().getCurrentContext());
//}

/** rb_ary_store
*
*/
11 changes: 7 additions & 4 deletions core/src/main/java/org/jruby/RubyBignum.java
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@
import org.jruby.runtime.JavaSites;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CachingCallSite;
import org.jruby.runtime.marshal.MarshalStream;
import org.jruby.runtime.marshal.UnmarshalStream;

@@ -1138,19 +1139,21 @@ public IRubyObject fdivFloat(ThreadContext context, RubyFloat y) {
@Override
public IRubyObject isNegative(ThreadContext context) {
Ruby runtime = context.runtime;
if (sites(context).basic_op_lt.retrieveCache(metaClass).method.isBuiltin()) {
CachingCallSite op_lt_site = sites(context).basic_op_lt;
if (op_lt_site.retrieveCache(metaClass).method.isBuiltin()) {
return runtime.newBoolean(value.signum() < 0);
}
return sites(context).basic_op_lt.call(context, this, this, RubyFixnum.zero(runtime));
return op_lt_site.call(context, this, this, RubyFixnum.zero(runtime));
}

@Override
public IRubyObject isPositive(ThreadContext context) {
Ruby runtime = context.runtime;
if (sites(context).basic_op_gt.retrieveCache(metaClass).method.isBuiltin()) {
CachingCallSite op_gt_site = sites(context).basic_op_gt;
if (op_gt_site.retrieveCache(metaClass).method.isBuiltin()) {
return runtime.newBoolean(value.signum() > 0);
}
return sites(context).basic_op_gt.call(context, this, this, RubyFixnum.zero(runtime));
return op_gt_site.call(context, this, this, RubyFixnum.zero(runtime));
}

@Override
11 changes: 7 additions & 4 deletions core/src/main/java/org/jruby/RubyFixnum.java
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@
import org.jruby.runtime.Signature;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CachingCallSite;
import org.jruby.runtime.marshal.UnmarshalStream;
import org.jruby.runtime.opto.OptoFactory;
import org.jruby.util.ByteList;
@@ -1337,19 +1338,21 @@ public IRubyObject fdivDouble(ThreadContext context, IRubyObject y) {
@Override
public IRubyObject isNegative(ThreadContext context) {
Ruby runtime = context.runtime;
if (sites(context).basic_op_lt.retrieveCache(metaClass).method.isBuiltin()) {
CachingCallSite op_lt_site = sites(context).basic_op_lt;
if (op_lt_site.retrieveCache(metaClass).method.isBuiltin()) {
return runtime.newBoolean(value < 0);
}
return sites(context).basic_op_lt.call(context, this, this, RubyFixnum.zero(runtime));
return op_lt_site.call(context, this, this, RubyFixnum.zero(runtime));
}

@Override
public IRubyObject isPositive(ThreadContext context) {
Ruby runtime = context.runtime;
if (sites(context).basic_op_gt.retrieveCache(metaClass).method.isBuiltin()) {
CachingCallSite op_gt_site = sites(context).basic_op_gt;
if (op_gt_site.retrieveCache(metaClass).method.isBuiltin()) {
return runtime.newBoolean(value > 0);
}
return sites(context).basic_op_gt.call(context, this, this, RubyFixnum.zero(runtime));
return op_gt_site.call(context, this, this, RubyFixnum.zero(runtime));
}

@Override
131 changes: 63 additions & 68 deletions core/src/main/java/org/jruby/RubyGlobal.java
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@
import jnr.posix.POSIX;

import org.jcodings.Encoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jruby.anno.JRubyMethod;
import org.jruby.common.IRubyWarnings.ID;
import org.jruby.internal.runtime.GlobalVariables;
@@ -53,7 +54,9 @@
import org.jruby.runtime.ReadonlyGlobalVariable;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.util.KCode;
import org.jruby.util.Numeric;
import org.jruby.util.OSEnvironment;
import org.jruby.util.RegexpOptions;
import org.jruby.util.cli.Options;
@@ -408,34 +411,30 @@ public CaseInsensitiveStringOnlyRubyHash(Ruby runtime, Map<RubyString, RubyStrin
super(runtime, valueMap, defaultValue, updateRealENV);
}

protected final boolean isCaseSensitive() { return false; }

@Override
public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
return asTainted(case_aware_op_aref(context, key, false));
IRubyObject val = super.op_aref(context, key);
val.setTaint(true);
return val;
}

@Override
public IRubyObject op_aset(ThreadContext context, IRubyObject key, IRubyObject value) {
return case_aware_op_aset(context, key, value, false);
}
private static final ByteList ENV = new ByteList(new byte[] {'E','N','V'}, USASCIIEncoding.INSTANCE, false);

@JRubyMethod(name = "to_s")
public IRubyObject to_s(ThreadContext context) {
return context.runtime.newString("ENV");
public RubyString to_s(ThreadContext context) {
return RubyString.newStringShared(context.runtime, ENV);
}

@Override
public IRubyObject to_s() {
return getRuntime().newString("ENV");
return RubyString.newStringShared(getRuntime(), ENV);
}

@JRubyMethod
public RubyHash to_h(){
return to_hash();
}

private IRubyObject asTainted(IRubyObject obj) {
obj.setTaint(true);
return obj;
return to_hash();
}

}
@@ -457,15 +456,12 @@ public StringOnlyRubyHash(Ruby runtime, Map<RubyString, RubyString> valueMap, IR
this.updateRealENV = updateRealENV;
}

protected boolean isCaseSensitive() { return true; }

public StringOnlyRubyHash(Ruby runtime, Map<RubyString, RubyString> valueMap, IRubyObject defaultValue) {
this(runtime, valueMap, defaultValue, false);
}

@Override
public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
return case_aware_op_aref(context, key, true);
}

@Override
public RubyHash to_hash() {
Ruby runtime = getRuntime();
@@ -474,9 +470,20 @@ public RubyHash to_hash() {
return hash;
}

@Override
protected RubyHashEntry internalGetEntry(IRubyObject key) {
if (size == 0) return NO_ENTRY;

if (!isCaseSensitive()) {
key = getCorrectKey(key.convertToString());
}

return super.internalGetEntry(key);
}

@Override
public IRubyObject op_aset(ThreadContext context, IRubyObject key, IRubyObject value) {
return case_aware_op_aset(context, key, value, true);
return case_aware_op_aset(context, key, value);
}

@Override
@@ -485,60 +492,51 @@ public IRubyObject op_aset19(ThreadContext context, IRubyObject key, IRubyObject
return op_aset(context, key, value);
}

protected IRubyObject case_aware_op_aref(ThreadContext context, IRubyObject key, boolean caseSensitive) {
if (! caseSensitive) {
key = getCorrectKey(key, context);
}
return super.op_aref(context, key);
}

protected IRubyObject case_aware_op_aset(ThreadContext context,
IRubyObject key, final IRubyObject value, boolean caseSensitive) {
if (!key.respondsTo("to_str")) {
throw getRuntime().newTypeError("can't convert " + key.getMetaClass() + " into String");
}
if (!value.respondsTo("to_str") && !value.isNil()) {
throw getRuntime().newTypeError("can't convert " + value.getMetaClass() + " into String");
}

if (!caseSensitive) {
key = getCorrectKey(key, context);
private IRubyObject case_aware_op_aset(ThreadContext context, IRubyObject key, final IRubyObject value) {
if (!isStringLike(key)) {
throw context.runtime.newTypeError("can't convert " + key.getMetaClass() + " into String");
}
RubyString keyAsStr = key.convertToString();
if (!isCaseSensitive()) key = keyAsStr = getCorrectKey(keyAsStr);

if (value.isNil()) {
if (value == context.nil) {
return super.delete(context, key, org.jruby.runtime.Block.NULL_BLOCK);
}
if (!isStringLike(value)) {
throw context.runtime.newTypeError("can't convert " + value.getMetaClass() + " into String");
}

IRubyObject keyAsStr = normalizeEnvString(context, key, Helpers.invoke(context, key, "to_str"));
IRubyObject valueAsStr = value.isNil() ? context.nil :
normalizeEnvString(context, key, Helpers.invoke(context, value, "to_str"));
RubyString valueAsStr = normalizeEnvString(context, keyAsStr, value.convertToString());

if (updateRealENV) {
POSIX posix = context.runtime.getPosix();
String keyAsJava = keyAsStr.asJavaString();
final POSIX posix = context.runtime.getPosix();
final String keyAsJava = keyAsStr.asJavaString();
// libc (un)setenv is not reentrant, so we need to synchronize across the entire JVM (JRUBY-5933)
if (valueAsStr == context.nil) {
synchronized (Object.class) { posix.unsetenv(keyAsJava); }
} else {
synchronized (Object.class) { posix.setenv(keyAsJava, valueAsStr.asJavaString(), 1); }
final String valueAsJava = valueAsStr.asJavaString();
synchronized (Object.class) { posix.setenv(keyAsJava, valueAsJava, 1); }
}
}

return super.op_aset(context, keyAsStr, valueAsStr);
}

private static boolean isStringLike(final IRubyObject obj) {
return obj instanceof RubyString || obj.respondsTo("to_str");
}

private RubyString getCorrectKey(IRubyObject key, ThreadContext context) {
RubyString originalKey = key.convertToString();
RubyString actualKey = originalKey;
Ruby runtime = context.runtime;
private RubyString getCorrectKey(final RubyString key) {
RubyString actualKey = key;
if (Platform.IS_WINDOWS) {
// this is a rather ugly hack, but similar to MRI. See hash.c:ruby_setenv and similar in MRI
// we search all keys for a case-insensitive match, and use that
RubyArray keys = super.keys();
final ThreadContext context = getRuntime().getCurrentContext();
final RubyArray keys = super.keys();
for (int i = 0; i < keys.size(); i++) {
RubyString candidateKey = keys.eltInternal(i).convertToString();
if (candidateKey.casecmp(context, originalKey).op_equal(context, RubyFixnum.zero(runtime)).isTrue()) {
if (Numeric.f_zero_p(context, candidateKey.casecmp(context, key))) {
actualKey = candidateKey;
break;
}
@@ -547,26 +545,23 @@ private RubyString getCorrectKey(IRubyObject key, ThreadContext context) {
return actualKey;
}

private IRubyObject normalizeEnvString(ThreadContext context, IRubyObject key, IRubyObject value) {
if (value instanceof RubyString) {
Ruby runtime = context.runtime;
RubyString valueStr = (RubyString) value;

// Ensure PATH is encoded like filesystem
if (Platform.IS_WINDOWS ?
key.toString().equalsIgnoreCase("PATH") :
key.toString().equals("PATH")) {
Encoding enc = runtime.getEncodingService().getFileSystemEncoding();
valueStr = EncodingUtils.strConvEnc(context, valueStr, valueStr.getEncoding(), enc);
} else {
valueStr = RubyString.newString(runtime, valueStr.toString(), runtime.getEncodingService().getLocaleEncoding());
}
private static RubyString normalizeEnvString(ThreadContext context, RubyString key, RubyString value) {
final Ruby runtime = context.runtime;

valueStr.setFrozen(true);
final RubyString valueStr;

return valueStr;
// Ensure PATH is encoded like filesystem
if (Platform.IS_WINDOWS ?
key.toString().equalsIgnoreCase("PATH") :
key.toString().equals("PATH")) {
Encoding enc = runtime.getEncodingService().getFileSystemEncoding();
valueStr = EncodingUtils.strConvEnc(context, value, value.getEncoding(), enc);
} else {
valueStr = RubyString.newString(runtime, value.toString(), runtime.getEncodingService().getLocaleEncoding());
}
return value;

valueStr.setFrozen(true);
return valueStr;
}
}

5 changes: 5 additions & 0 deletions core/src/main/java/org/jruby/RubyRange.java
Original file line number Diff line number Diff line change
@@ -292,6 +292,11 @@ public IRubyObject initialize_copy(ThreadContext context, IRubyObject original)
return context.nil;
}

@Override
public RubyFixnum hash() {
return hash(getRuntime().getCurrentContext());
}

@JRubyMethod(name = "hash")
public RubyFixnum hash(ThreadContext context) {
Ruby runtime = context.runtime;
11 changes: 10 additions & 1 deletion core/src/main/java/org/jruby/RubyRational.java
Original file line number Diff line number Diff line change
@@ -1224,12 +1224,21 @@ public RubyString to_s(ThreadContext context) {
return str;
}

@Override
public IRubyObject inspect() {
return inspectImpl(getRuntime());
}

/** nurat_inspect
*
*/
@JRubyMethod(name = "inspect")
public RubyString inspect(ThreadContext context) {
RubyString str = RubyString.newString(context.runtime, new ByteList(12), USASCIIEncoding.INSTANCE);
return inspectImpl(context.runtime);
}

private RubyString inspectImpl(Ruby runtime) {
RubyString str = RubyString.newString(runtime, new ByteList(12), USASCIIEncoding.INSTANCE);
str.cat((byte)'(');
str.append(num.inspect());
str.cat((byte)'/');
Loading