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: ef55159a1b3b
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a4164673e670
Choose a head ref
  • 2 commits
  • 5 files changed
  • 1 contributor

Commits on Mar 17, 2015

  1. Verified

    This commit was signed with the committer’s verified signature.
    headius Charles Oliver Nutter
    Copy the full SHA
    714712e View commit details
  2. Copy the full SHA
    a416467 View commit details
24 changes: 4 additions & 20 deletions core/src/main/java/org/jruby/RubyString.java
Original file line number Diff line number Diff line change
@@ -311,22 +311,6 @@ private Encoding checkDummyEncoding() {
return enc;
}

private boolean isComparableWith(RubyString other) {
ByteList otherValue = other.value;
if (value.getEncoding() == otherValue.getEncoding() ||
value.getRealSize() == 0 || otherValue.getRealSize() == 0) return true;
return isComparableViaCodeRangeWith(other);
}

private boolean isComparableViaCodeRangeWith(RubyString other) {
int cr1 = scanForCodeRange();
int cr2 = other.scanForCodeRange();

if (cr1 == CR_7BIT && (cr2 == CR_7BIT || other.value.getEncoding().isAsciiCompatible())) return true;
if (cr2 == CR_7BIT && value.getEncoding().isAsciiCompatible()) return true;
return false;
}

public final int strLength() {
if (StringSupport.isSingleByteOptimizable(this, value.getEncoding())) return value.getRealSize();
return StringSupport.strLengthFromRubyString(this);
@@ -351,7 +335,7 @@ public final boolean eql(IRubyObject other) {
// rb_str_hash_cmp
private boolean eql19(Ruby runtime, IRubyObject other) {
RubyString otherString = (RubyString)other;
return isComparableWith(otherString) && value.equal(((RubyString)other).value);
return StringSupport.areComparable(this, otherString) && value.equal(((RubyString)other).value);
}

public RubyString(Ruby runtime, RubyClass rubyClass) {
@@ -1104,7 +1088,7 @@ public IRubyObject op_equal19(ThreadContext context, IRubyObject other) {
if (this == other) return runtime.getTrue();
if (other instanceof RubyString) {
RubyString otherString = (RubyString)other;
return isComparableWith(otherString) && value.equal(otherString.value) ? runtime.getTrue() : runtime.getFalse();
return StringSupport.areComparable(this, otherString) && value.equal(otherString.value) ? runtime.getTrue() : runtime.getFalse();
}
return op_equalCommon(context, other);
}
@@ -1265,7 +1249,7 @@ public static RubyString objAsString(ThreadContext context, IRubyObject obj) {
*/
public final int op_cmp(RubyString other) {
int ret = value.cmp(other.value);
if (ret == 0 && !isComparableWith(other)) {
if (ret == 0 && !StringSupport.areComparable(this, other)) {
return value.getEncoding().getIndex() > other.value.getEncoding().getIndex() ? 1 : -1;
}
return ret;
@@ -1738,7 +1722,7 @@ public IRubyObject str_eql_p19(ThreadContext context, IRubyObject other) {
Ruby runtime = context.runtime;
if (other instanceof RubyString) {
RubyString otherString = (RubyString)other;
if (isComparableWith(otherString) && value.equal(otherString.value)) return runtime.getTrue();
if (StringSupport.areComparable(this, otherString) && value.equal(otherString.value)) return runtime.getTrue();
}
return runtime.getFalse();
}
16 changes: 16 additions & 0 deletions core/src/main/java/org/jruby/util/StringSupport.java
Original file line number Diff line number Diff line change
@@ -1374,4 +1374,20 @@ public static ByteList addByteLists(ByteList value1, ByteList value2) {
System.arraycopy(value2.getUnsafeBytes(), value2.getBegin(), result.getUnsafeBytes(), value1.getRealSize(), value2.getRealSize());
return result;
}

public static boolean areComparable(CodeRangeable string, CodeRangeable other) {
ByteList otherValue = other.getByteList();
if (string.getByteList().getEncoding() == otherValue.getEncoding() ||
string.getByteList().getRealSize() == 0 || otherValue.getRealSize() == 0) return true;
return areComparableViaCodeRange(string, other);
}

public static boolean areComparableViaCodeRange(CodeRangeable string, CodeRangeable other) {
int cr1 = string.scanForCodeRange();
int cr2 = other.scanForCodeRange();

if (cr1 == CR_7BIT && (cr2 == CR_7BIT || other.getByteList().getEncoding().isAsciiCompatible())) return true;
if (cr2 == CR_7BIT && string.getByteList().getEncoding().isAsciiCompatible()) return true;
return false;
}
}
4 changes: 0 additions & 4 deletions spec/truffle/tags/core/string/comparison_tags.txt

This file was deleted.

130 changes: 130 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/nodes/cast/CmpIntNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright (c) 2013, 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*
* Contains code modified from JRuby's RubyComparable.java
*
* Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
* Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
* Copyright (C) 2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
* Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
* Copyright (C) 2005 Charles O Nutter <headius@headius.com>
* Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
* Copyright (C) 2006 Thomas E Enebo <enebo@acm.org>
*/

package org.jruby.truffle.nodes.cast;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.RubyBignum;
import org.jruby.truffle.runtime.core.RubyNilClass;

/**
* This is a port of MRI's rb_cmpint, as taken from RubyComparable and broken out into specialized nodes.
*/

@NodeChildren({
@NodeChild(value = "value"),
@NodeChild(value = "receiver"),
@NodeChild(value = "other")
})
public abstract class CmpIntNode extends RubyNode {

@Child private CallDispatchHeadNode gtNode;
@Child private CallDispatchHeadNode ltNode;

public CmpIntNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public CmpIntNode(CmpIntNode prev) {
super(prev);
gtNode = prev.gtNode;
ltNode = prev.ltNode;
}

@Specialization
public int cmpInt(int value, Object receiver, Object other) {
if (value > 0) {
return 1;
}

if (value < 0) {
return -1;
}

return 0;
}

@Specialization
public int cmpLong(long value, Object receiver, Object other) {
if (value > 0) {
return 1;
}

if (value < 0) {
return -1;
}

return 0;
}

@Specialization
public int cmpBignum(RubyBignum value, Object receiver, Object other) {
return value.bigIntegerValue().signum();
}

@Specialization
public int cmpNil(RubyNilClass value, Object receiver, Object other) {
throw new RaiseException(
getContext().getCoreLibrary().argumentError(
String.format("comparison of %s with %s failed",
getContext().getCoreLibrary().getLogicalClass(receiver).getName(),
getContext().getCoreLibrary().getLogicalClass(other).getName()), this)
);
}

@Specialization(guards = {
"!isInteger(value)",
"!isLong(value)",
"!isRubyBignum(value)",
"!isRubyNilClass(value)" })
public int cmpObject(VirtualFrame frame, Object value, Object receiver, Object other) {
if (gtNode == null) {
CompilerDirectives.transferToInterpreter();
gtNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

if (gtNode.callBoolean(frame, value, ">", null, 0)) {
return 1;
}

if (ltNode == null) {
CompilerDirectives.transferToInterpreter();
ltNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

if (ltNode.callBoolean(frame, value, "<", null, 0)) {
return -1;
}

return 0;
}

public abstract int executeIntegerFixnum(VirtualFrame frame, Object value, Object receiver, Object other);
}
86 changes: 59 additions & 27 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java
Original file line number Diff line number Diff line change
@@ -31,26 +31,24 @@
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.BranchProfile;

import com.oracle.truffle.api.utilities.ConditionProfile;
import org.jcodings.Encoding;
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.joni.Matcher;
import org.joni.Option;
import org.joni.Region;
import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.cast.CmpIntNode;
import org.jruby.truffle.nodes.cast.CmpIntNodeFactory;
import org.jruby.truffle.nodes.cast.TaintResultNode;
import org.jruby.truffle.nodes.coerce.ToIntNode;
import org.jruby.truffle.nodes.coerce.ToIntNodeFactory;
import org.jruby.truffle.nodes.coerce.ToStrNode;
import org.jruby.truffle.nodes.coerce.ToStrNodeFactory;
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.nodes.objects.IsFrozenNode;
import org.jruby.truffle.nodes.objects.IsFrozenNodeFactory;
@@ -61,21 +59,14 @@
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.*;
import org.jruby.truffle.runtime.rubinius.RubiniusByteArray;
import org.jruby.truffle.runtime.util.ArrayUtils;
import org.jruby.util.ByteList;
import org.jruby.util.CodeRangeSupport;
import org.jruby.util.Pack;
import org.jruby.util.StringSupport;
import org.jruby.util.io.EncodingUtils;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;

@CoreClass(name = "String")
public abstract class StringNodes {
@@ -222,6 +213,10 @@ public boolean equal(VirtualFrame frame, RubyString a, Object b) {
@CoreMethod(names = "<=>", required = 1)
public abstract static class CompareNode extends CoreMethodNode {

@Child private CallDispatchHeadNode cmpNode;
@Child private CmpIntNode cmpIntNode;
@Child private KernelNodes.RespondToNode respondToCmpNode;
@Child private KernelNodes.RespondToNode respondToToStrNode;
@Child private ToStrNode toStrNode;

public CompareNode(RubyContext context, SourceSection sourceSection) {
@@ -230,43 +225,80 @@ public CompareNode(RubyContext context, SourceSection sourceSection) {

public CompareNode(CompareNode prev) {
super(prev);
cmpNode = prev.cmpNode;
cmpIntNode = prev.cmpIntNode;
respondToCmpNode = prev.respondToCmpNode;
respondToToStrNode = prev.respondToToStrNode;
toStrNode = prev.toStrNode;
}

@Specialization
public int compare(RubyString a, RubyString b) {
notDesignedForCompilation();
// Taken from org.jruby.RubyString#op_cmp

final int result = a.toString().compareTo(b.toString());
final int ret = a.getByteList().cmp(b.getByteList());

if (result < 0) {
return -1;
} else if (result > 0) {
return 1;
if ((ret == 0) && !StringSupport.areComparable(a, b)) {
return a.getByteList().getEncoding().getIndex() > b.getByteList().getEncoding().getIndex() ? 1 : -1;
}

return 0;
return ret;
}

@Specialization(guards = "!isRubyString(arguments[1])")
public Object compare(VirtualFrame frame, RubyString a, Object b) {
notDesignedForCompilation();

if (toStrNode == null) {
if (respondToToStrNode == null) {
CompilerDirectives.transferToInterpreter();
toStrNode = insert(ToStrNodeFactory.create(getContext(), getSourceSection(), null));
respondToToStrNode = insert(KernelNodesFactory.RespondToNodeFactory.create(getContext(), getSourceSection(), new RubyNode[] { null, null, null }));
}

try {
final RubyString coerced = toStrNode.executeRubyString(frame, b);
if (respondToToStrNode.doesRespondTo(frame, b, getContext().makeString("to_str"), false)) {
if (toStrNode == null) {
CompilerDirectives.transferToInterpreter();
toStrNode = insert(ToStrNodeFactory.create(getContext(), getSourceSection(), null));
}

try {
final RubyString coerced = toStrNode.executeRubyString(frame, b);

return compare(a, coerced);
} catch (RaiseException e) {
if (e.getRubyException().getLogicalClass() == getContext().getCoreLibrary().getTypeErrorClass()) {
return compare(a, coerced);
} catch (RaiseException e) {
if (e.getRubyException().getLogicalClass() == getContext().getCoreLibrary().getTypeErrorClass()) {
return nil();
} else {
throw e;
}
}
}

if (respondToCmpNode == null) {
CompilerDirectives.transferToInterpreter();
respondToCmpNode = insert(KernelNodesFactory.RespondToNodeFactory.create(getContext(), getSourceSection(), new RubyNode[] { null, null, null }));
}

if (respondToCmpNode.doesRespondTo(frame, b, getContext().makeString("<=>"), false)) {
if (cmpNode == null) {
CompilerDirectives.transferToInterpreter();
cmpNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

final Object cmpResult = cmpNode.call(frame, b, "<=>", null, a);

if (cmpResult == nil()) {
return nil();
} else {
throw e;
}

if (cmpIntNode == null) {
CompilerDirectives.transferToInterpreter();
cmpIntNode = insert(CmpIntNodeFactory.create(getContext(), getSourceSection(), null, null, null));
}

return -(cmpIntNode.executeIntegerFixnum(frame, cmpResult, a, b));
}

return nil();
}
}