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

Commits on Feb 17, 2015

  1. Copy the full SHA
    26b21c5 View commit details
  2. Copy the full SHA
    21c3f69 View commit details
  3. [Truffle] Rubinius needs a way to access the string matched against i…

    …n a regexp match via the MatchData.
    nirvdrum committed Feb 17, 2015
    7
    Copy the full SHA
    2e1c959 View commit details
  4. [Truffle] Moved String#gsub and String#gsub! to Rubinius.

    Moving this to Rubinius required implementing a couple Rubinius primitives and filling in some Rubinius specific data structures, ivars, and methods.  While a bit large, this commit includes the necessary machinery to get the methods working in Rubinius.
    nirvdrum committed Feb 17, 2015
    Copy the full SHA
    824bacc View commit details
  5. Copy the full SHA
    3482b59 View commit details
1 change: 0 additions & 1 deletion spec/truffle/tags/core/string/byteslice_tags.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
fails:String#byteslice returns the character code of the character at the given index
fails:String#byteslice returns nil if index is outside of self
fails:String#byteslice calls to_int on the given index
fails:String#byteslice with index, length returns the substring starting at the given index with the given length
fails:String#byteslice with index, length always taints resulting strings when self is tainted
fails:String#byteslice with index, length returns nil if the offset falls outside of self
fails:String#byteslice with index, length returns nil if the length is negative
16 changes: 1 addition & 15 deletions spec/truffle/tags/core/string/gsub_tags.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
fails:String#gsub with pattern and replacement replaces \1 sequences with the regexp's corresponding capture
fails:String#gsub with pattern and replacement treats \1 sequences without corresponding captures as empty strings
fails:String#gsub with pattern and replacement replaces \& and \0 with the complete match
fails:String#gsub with pattern and replacement replaces \` with everything before the current match
fails:String#gsub with pattern and replacement replaces \' with everything after the current match
fails:String#gsub with pattern and replacement replaces \+ with the last paren that actually matched
fails:String#gsub with pattern and replacement treats \+ as an empty string if there was no captures
fails:String#gsub with pattern and replacement maps \\ in replacement to \
fails:String#gsub with pattern and replacement replaces \k named backreferences with the regexp's corresponding capture
fails:String#gsub with pattern and replacement taints the result if the original string or replacement is tainted
fails:String#gsub with pattern and replacement untrusts the result if the original string or replacement is untrusted
fails:String#gsub with pattern and replacement tries to convert pattern to a string using to_str
fails:String#gsub with pattern and replacement tries to convert replacement to a string using to_str
fails:String#gsub with pattern and replacement sets $~ to MatchData of last match and nil when there's none
fails:String#gsub with pattern and Hash returns a copy of self with all occurrences of pattern replaced with the value of the corresponding hash key
fails:String#gsub with pattern and Hash ignores keys that don't correspond to matches
fails:String#gsub with pattern and Hash returns an empty string if the pattern matches but the hash specifies no replacements
@@ -48,23 +38,19 @@ fails:String#gsub with pattern and block untrusts the result if the original str
fails:String#gsub with pattern and block uses the compatible encoding if they are compatible
fails:String#gsub with pattern and block raises an Encoding::CompatibilityError if the encodings are not compatible
fails:String#gsub with pattern and block replaces the incompatible part properly even if the encodings are not compatible
fails:String#gsub with pattern and block raises an ArgumentError if encoding is not valid
fails:String#gsub! with pattern and replacement modifies self in place and returns self
fails:String#gsub! with pattern and replacement taints self if replacement is tainted
fails:String#gsub! with pattern and replacement untrusts self if replacement is untrusted
fails:String#gsub! with pattern and replacement returns nil if no modifications were made
fails:String#gsub! with pattern and replacement raises a RuntimeError when self is frozen
fails:String#gsub! with pattern and block modifies self in place and returns self
fails:String#gsub! with pattern and block taints self if block's result is tainted
fails:String#gsub! with pattern and block untrusts self if block's result is untrusted
fails:String#gsub! with pattern and block returns nil if no modifications were made
fails:String#gsub! with pattern and block raises a RuntimeError when self is frozen
fails:String#gsub! with pattern and block uses the compatible encoding if they are compatible
fails:String#gsub! with pattern and block raises an Encoding::CompatibilityError if the encodings are not compatible
fails:String#gsub! with pattern and block replaces the incompatible part properly even if the encodings are not compatible
fails:String#gsub! with pattern and block raises an ArgumentError if encoding is not valid
fails:String#gsub with pattern and replacement respects $KCODE when the pattern collapses
fails:String#gsub with pattern and replacement handles pattern collapse without $KCODE
fails:String#gsub with pattern and block returns a copy of self with all occurrences of pattern replaced with the block's return value
fails:String#gsub with pattern and block doesn't interpolate special sequences like \1 for the block's return value
fails:String#gsub with pattern and block converts the block's return value to a string using to_s
fails:String#gsub with pattern and replacement returns subclass instances when called on a subclass
7 changes: 0 additions & 7 deletions spec/truffle/tags/core/string/replace_tags.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
fails:String#replace returns self
fails:String#replace replaces the content of self with other
fails:String#replace taints self if other is tainted
fails:String#replace does not untaint self if other is untainted
fails:String#replace untrusts self if other is untrusted
fails:String#replace does not trust self if other is trusted
fails:String#replace replaces the encoding of self with that of other
fails:String#replace carries over the encoding invalidity
fails:String#replace tries to convert other to string using to_str
fails:String#replace raises a TypeError if other can't be converted to string
fails:String#replace raises a RuntimeError on a frozen instance that is modified
fails:String#replace raises a RuntimeError on a frozen instance when self-replacing
3 changes: 0 additions & 3 deletions spec/truffle/tags/rubysl/rubysl-erb/spec/util/h_tags.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
fails:ERB::Util.h escape (& < > " ') to (&amp; &lt; &gt; &quot; &#39;)
fails:ERB::Util.h not escape characters except (& < > " ')
fails:ERB::Util.h return empty string when argument is nil
fails:ERB::Util.h returns string when argument is number
fails:ERB::Util.h returns string when argument is boolean
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
fails:ERB::Util.html_escape escape (& < > " ') to (&amp; &lt; &gt; &quot; &#39;)
fails:ERB::Util.html_escape not escape characters except (& < > " ')
fails:ERB::Util.html_escape return empty string when argument is nil
fails:ERB::Util.html_escape returns string when argument is number
fails:ERB::Util.html_escape returns string when argument is boolean
3 changes: 0 additions & 3 deletions spec/truffle/tags/rubysl/rubysl-erb/spec/util/u_tags.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
fails:ERB::Util.u encode characters
fails:ERB::Util.u encode unicode string
fails:ERB::Util.u returns empty string when argument is nil
fails:ERB::Util.u returns string when argument is number
fails:ERB::Util.u returns string when argument is boolean
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
fails:ERB::Util.url_encode encode characters
fails:ERB::Util.url_encode encode unicode string
fails:ERB::Util.url_encode returns empty string when argument is nil
fails:ERB::Util.url_encode returns string when argument is number
fails:ERB::Util.url_encode returns string when argument is boolean
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.ConditionProfile;
import org.joni.exception.ValueException;
import org.jruby.runtime.Visibility;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.RubyArray;
@@ -261,4 +262,21 @@ public RubyArray valuesAt(RubyMatchData matchData, Object[] args) {

}

@CoreMethod(names = "__rubinius_source__", visibility = Visibility.PRIVATE)
public abstract static class RubiniusSourceNode extends CoreMethodNode {

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

public RubiniusSourceNode(RubiniusSourceNode prev) {
super(prev);
}

@Specialization
public RubyString rubiniusSource(RubyMatchData matchData) {
return matchData.getSource();
}
}

}
Original file line number Diff line number Diff line change
@@ -85,7 +85,7 @@ public CaseEqualNode(CaseEqualNode prev) {
public Object match(RubyRegexp regexp, RubyString string) {
notDesignedForCompilation();

return regexp.matchCommon(string.getBytes(), true, false) != getContext().getCoreLibrary().getNilObject();
return regexp.matchCommon(string, true, false) != getContext().getCoreLibrary().getNilObject();
}

}
@@ -103,7 +103,7 @@ public MatchOperatorNode(MatchOperatorNode prev) {

@Specialization
public Object match(RubyRegexp regexp, RubyString string) {
return regexp.matchCommon(string.getBytes(), true, true);
return regexp.matchCommon(string, true, true);
}

@Specialization
@@ -172,7 +172,7 @@ public MatchNode(MatchNode prev) {

@Specialization
public Object match(RubyRegexp regexp, RubyString string) {
return regexp.matchCommon(string.getBytes(), false, false);
return regexp.matchCommon(string, false, false);
}

}
161 changes: 46 additions & 115 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java
Original file line number Diff line number Diff line change
@@ -357,7 +357,7 @@ public Object slice(RubyString string, int start, int length) {
public Object slice(RubyString string, RubyRegexp regexp, UndefinedPlaceholder capture) {
notDesignedForCompilation();

final Object matchData = regexp.matchCommon(string.getBytes(), false, false);
final Object matchData = regexp.matchCommon(string, false, false);

if (matchData == getContext().getCoreLibrary().getNilObject()) {
return matchData;
@@ -479,7 +479,7 @@ public MatchOperatorNode(MatchOperatorNode prev) {

@Specialization
public Object match(RubyString string, RubyRegexp regexp) {
return regexp.matchCommon(string.getBytes(), true, false);
return regexp.matchCommon(string, true, false);
}
}

@@ -598,11 +598,16 @@ public Object byteSlice(RubyString string, int index, int length) {

final int normalizedIndex = string.normalizeIndex(index);

if (normalizedIndex > bytes.length() || normalizedIndex + length > bytes.length()) {
if (normalizedIndex > bytes.length()) {
return getContext().getCoreLibrary().getNilObject();
}

final byte[] copiedBytes = Arrays.copyOfRange(bytes.getUnsafeBytes(), index, index + length);
int rangeEnd = normalizedIndex + length;
if (rangeEnd > bytes.getRealSize()) {
rangeEnd = bytes.getRealSize();
}

final byte[] copiedBytes = Arrays.copyOfRange(bytes.getUnsafeBytes(), normalizedIndex, rangeEnd);

return new RubyString(getContext().getCoreLibrary().getStringClass(), new ByteList(copiedBytes, string.getBytes().getEncoding()));
}
@@ -925,113 +930,6 @@ public RubyString forceEncoding(RubyString string, RubyEncoding encoding) {

}

@CoreMethod(names = "gsub!", required = 1, optional = 1, needsBlock = true)
public abstract static class GsubBangNode extends RegexpNodes.EscapingYieldingNode {

@Child private CallDispatchHeadNode toS;

public GsubBangNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
toS = DispatchHeadNodeFactory.createMethodCall(context);
}

public GsubBangNode(GsubBangNode prev) {
super(prev);
toS = prev.toS;
}

@Specialization
public RubyString gsubBang(VirtualFrame frame, RubyString string, RubyString regexpString, RubyString replacement, UndefinedPlaceholder block) {
notDesignedForCompilation();

final RubyRegexp regexp = new RubyRegexp(this, getContext().getCoreLibrary().getRegexpClass(), escape(frame, regexpString).getBytes(), Option.DEFAULT);
return gsubBang(string, regexp, replacement, block);
}

@Specialization
public RubyString gsubBang(RubyString string, RubyRegexp regexp, RubyString replacement, @SuppressWarnings("unused") UndefinedPlaceholder block) {
notDesignedForCompilation();

string.set(regexp.gsub(string, replacement.toString()).getBytes());
return string;
}

@Specialization
public RubyString gsubBang(VirtualFrame frame, RubyString string, RubyString regexpString, RubyString replacement, RubyProc block) {
notDesignedForCompilation();

final RubyRegexp regexp = new RubyRegexp(this, getContext().getCoreLibrary().getRegexpClass(), escape(frame, regexpString).getBytes(), Option.DEFAULT);
return gsubBang(string, regexp, replacement, block);
}

@Specialization
public RubyString gsubBang(RubyString string, RubyRegexp regexp, RubyString replacement, @SuppressWarnings("unused") RubyProc block) {
notDesignedForCompilation();

string.set(regexp.gsub(string, replacement.toString()).getBytes());
return string;
}

@Specialization
public RubyString gsubBang(VirtualFrame frame, RubyString string, RubyString regexpString, @SuppressWarnings("unused") UndefinedPlaceholder replacement, RubyProc block) {
notDesignedForCompilation();

final RubyRegexp regexp = new RubyRegexp(this, getContext().getCoreLibrary().getRegexpClass(), escape(frame, regexpString).getBytes(), Option.DEFAULT);
return gsubBang(frame, string, regexp, replacement, block);
}

@Specialization
public RubyString gsubBang(VirtualFrame frame, RubyString string, RubyRegexp regexp, @SuppressWarnings("unused") UndefinedPlaceholder replacement, RubyProc block) {
notDesignedForCompilation();

final RubyContext context = getContext();

final byte[] stringBytes = string.getBytes().bytes();
final Encoding encoding = string.getBytes().getEncoding();
final Matcher matcher = regexp.getRegex().matcher(stringBytes);

int p = string.getBytes().getBegin();
int end = 0;
int range = p + string.getBytes().getRealSize();
int lastMatchEnd = 0;

// We only ever care about the entire matched string, not each of the matched parts, so we can hard-code the index.
int matchedStringIndex = 0;

final StringBuilder builder = new StringBuilder();

while (true) {
Object matchData = regexp.matchCommon(string.getBytes(), false, true, matcher, p + end, range);

if (matchData == context.getCoreLibrary().getNilObject()) {
builder.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(stringBytes, lastMatchEnd, range - lastMatchEnd)));

break;
}

Region region = matcher.getEagerRegion();

RubyMatchData md = (RubyMatchData) matchData;
Object[] values = md.getValues();

int regionStart = region.beg[matchedStringIndex];
int regionEnd = region.end[matchedStringIndex];

// TODO (nirvdrum Dec. 24, 2014): There's probably a better way of doing this than converting back and forth between String and RubyString.
builder.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(stringBytes, lastMatchEnd, regionStart - lastMatchEnd)));

Object yieldResult = yield(frame, block, values[matchedStringIndex]);
builder.append(toS.call(frame, yieldResult, "to_s", null).toString());

lastMatchEnd = regionEnd;
end = StringSupport.positionEndForScan(string.getBytes(), matcher, encoding, p, range);
}

string.set(context.makeString(builder.toString()).getBytes());
return string;
}
}

@CoreMethod(names = "getbyte", required = 1)
public abstract static class GetByteNode extends CoreMethodNode {

@@ -1227,12 +1125,12 @@ public Object match(RubyString string, RubyString regexpString) {
notDesignedForCompilation();

final RubyRegexp regexp = new RubyRegexp(this, getContext().getCoreLibrary().getRegexpClass(), regexpString.getBytes(), Option.DEFAULT);
return regexp.matchCommon(string.getBytes(), false, false);
return regexp.matchCommon(string, false, false);
}

@Specialization
public Object match(RubyString string, RubyRegexp regexp) {
return regexp.matchCommon(string.getBytes(), false, false);
return regexp.matchCommon(string, false, false);
}
}

@@ -1254,6 +1152,39 @@ public int ord(RubyString string) {
}
}

@CoreMethod(names = "replace", required = 1)
public abstract static class ReplaceNode extends CoreMethodNode {

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

public ReplaceNode(ReplaceNode prev) {
super(prev);
}

@Specialization
public RubyString replace(RubyString string, RubyString other) {
notDesignedForCompilation();

if (string.isFrozen()) {
CompilerDirectives.transferToInterpreter();

throw new RaiseException(getContext().getCoreLibrary().frozenError("String", this));
}

if (string == other) {
return string;
}

string.getByteList().replace(other.getByteList().bytes());
string.setCodeRange(other.getCodeRange());

return string;
}

}

@CoreMethod(names = "rindex", required = 1, optional = 1, lowerFixnumParameters = 1)
public abstract static class RindexNode extends CoreMethodNode {

@@ -1466,7 +1397,7 @@ public RubyString scan(VirtualFrame frame, RubyString string, RubyRegexp regexp,

if (regexp.getRegex().numberOfCaptures() == 0) {
while (true) {
Object matchData = regexp.matchCommon(string.getBytes(), false, true, matcher, p + end, range);
Object matchData = regexp.matchCommon(string, false, true, matcher, p + end, range);

if (matchData == context.getCoreLibrary().getNilObject()) {
break;
@@ -1486,7 +1417,7 @@ public RubyString scan(VirtualFrame frame, RubyString string, RubyRegexp regexp,
regexp.setThread("$~", lastGoodMatchData);
} else {
while (true) {
Object matchData = regexp.matchCommon(string.getBytes(), false, true, matcher, p + end, stringBytes.length);
Object matchData = regexp.matchCommon(string, false, true, matcher, p + end, stringBytes.length);

if (matchData == context.getCoreLibrary().getNilObject()) {
break;
Loading