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

Commits on Feb 9, 2015

  1. Copy the full SHA
    dedeaf3 View commit details
  2. Copy the full SHA
    fc8b400 View commit details
  3. Copy the full SHA
    0207cf8 View commit details
  4. 2
    Copy the full SHA
    4d11e21 View commit details
10 changes: 0 additions & 10 deletions spec/truffle/tags/core/string/each_line_tags.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,4 @@
fails:String#each_line splits using default newline separator when none is specified
fails:String#each_line splits self using the supplied record separator and passes each substring to the block
fails:String#each_line taints substrings that are passed to the block if self is tainted
fails:String#each_line passes self as a whole to the block if the separator is nil
fails:String#each_line yields paragraphs (broken by 2 or more successive newlines) when passed ''
fails:String#each_line uses $/ as the separator when none is given
fails:String#each_line yields subclass instances for subclasses
fails:String#each_line returns self
fails:String#each_line tries to convert the separator to a string using to_str
fails:String#each_line does not care if the string is modified while substituting
fails:String#each_line raises a TypeError when the separator can't be converted to a string
fails:String#each_line accepts a string separator
fails:String#each_line raises a TypeError when the separator is a symbol
fails:String#each_line returns an enumerator when no block given
Original file line number Diff line number Diff line change
@@ -549,7 +549,7 @@ public Object byteSlice(RubyString string, int index, int length) {
return getContext().getCoreLibrary().getNilObject();
}

final byte[] copiedBytes = Arrays.copyOfRange(bytes.getUnsafeBytes(), index, index + 1);
final byte[] copiedBytes = Arrays.copyOfRange(bytes.getUnsafeBytes(), index, index + length);

return new RubyString(getContext().getCoreLibrary().getStringClass(), new ByteList(copiedBytes, string.getBytes().getEncoding()));
}
@@ -742,59 +742,6 @@ public RubyString eachChar(VirtualFrame frame, RubyString string, RubyProc block

}

@CoreMethod(names = "each_line", optional = 1)
public abstract static class EachLineNode extends YieldingCoreMethodNode {

@Child private ToStrNode toStrNode;

public EachLineNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
toStrNode = ToStrNodeFactory.create(context, sourceSection, null);
}

public EachLineNode(EachLineNode prev) {
super(prev);
toStrNode = prev.toStrNode;
}

@Specialization
public RubyArray eachLine(VirtualFrame frame, RubyString string, @SuppressWarnings("unused") UndefinedPlaceholder separator) {
notDesignedForCompilation();

final RubyBasicObject globals = getContext().getCoreLibrary().getGlobalVariablesObject();
final RubyString recordSeparator = (RubyString) globals.getInstanceVariable("$/");
return eachLine(frame, string, recordSeparator);
}

@Specialization(guards = "!isUndefinedPlaceholder(arguments[1])")
public RubyArray eachLine(VirtualFrame frame, RubyString string, Object separator) {
notDesignedForCompilation();

final List<Object> lines = new ArrayList<>();

String str = string.toString();
String sep = toStrNode.executeRubyString(frame, separator).toString();

int start = 0;

while (start < str.length()) {
int end = str.indexOf(sep, start);

if (end == -1) {
lines.add(getContext().makeString(str.substring(start)));
break;
}

String line = str.substring(start, end + sep.length());
start = end + sep.length();

lines.add(getContext().makeString(line));
}

return RubyArray.fromObjects(getContext().getCoreLibrary().getArrayClass(), lines.toArray(new Object[lines.size()]));
}
}

@CoreMethod(names = "empty?")
public abstract static class EmptyNode extends CoreMethodNode {

Original file line number Diff line number Diff line change
@@ -11,10 +11,14 @@

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.ConditionProfile;
import org.jcodings.exception.EncodingException;
import org.jcodings.specific.ASCIIEncoding;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.core.StringNodes;
import org.jruby.truffle.nodes.core.StringNodesFactory;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.*;
@@ -26,6 +30,28 @@
*/
public abstract class StringPrimitiveNodes {

@RubiniusPrimitive(name = "string_byte_substring")
public static abstract class StringByteSubstringPrimitiveNode extends RubiniusPrimitiveNode {

@Child private StringNodes.ByteSliceNode byteSliceNode;

public StringByteSubstringPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
byteSliceNode = StringNodesFactory.ByteSliceNodeFactory.create(context, sourceSection, new RubyNode[] {});
}

public StringByteSubstringPrimitiveNode(StringByteSubstringPrimitiveNode prev) {
super(prev);
byteSliceNode = prev.byteSliceNode;
}

@Specialization
public Object stringByteSubstring(VirtualFrame frame, RubyString string, int index, int length) {
return byteSliceNode.byteSlice(string, index, length);
}

}

@RubiniusPrimitive(name = "string_check_null_safe", needsSelf = false)
public static abstract class StringCheckNullSafePrimitiveNode extends RubiniusPrimitiveNode {

Original file line number Diff line number Diff line change
@@ -1518,6 +1518,23 @@ public RubyNode visitInstVarNode(org.jruby.ast.InstVarNode node) {
}
}

if (sourceSection.getSource().getPath().equals("core:/core/rubinius/common/string.rb")) {
if (nameWithoutSigil.equals("@num_bytes")) {
return new RubyCallNode(context, sourceSection,
"bytesize",
new SelfNode(context, sourceSection),
null,
false);
} else if (nameWithoutSigil.equals("@data")) {
return new RubyCallNode(context, sourceSection,
"bytes",
new SelfNode(context, sourceSection),
null,
false);
}
}


final RubyNode receiver = new SelfNode(context, sourceSection);

return new ReadInstanceVariableNode(context, sourceSection, nameWithoutSigil, receiver, false);
33 changes: 33 additions & 0 deletions truffle/src/main/ruby/core/rubinius/bootstrap/string.rb
Original file line number Diff line number Diff line change
@@ -38,4 +38,37 @@ def find_string(pattern, start)
raise PrimitiveFailure, "String#find_string primitive failed"
end

def byteslice(index_or_range, length=undefined)
Rubinius.primitive :string_byte_substring

if index_or_range.kind_of? Range
index = Rubinius::Type.coerce_to index_or_range.begin, Fixnum, :to_int
index += @num_bytes if index < 0
return if index < 0 or index > @num_bytes

finish = Rubinius::Type.coerce_to index_or_range.end, Fixnum, :to_int
finish += @num_bytes if finish < 0

finish += 1 unless index_or_range.exclude_end?
length = finish - index

return byteslice 0, 0 if length < 0
else
index = Rubinius::Type.coerce_to index_or_range, Fixnum, :to_int
index += @num_bytes if index < 0

if undefined.equal?(length)
return if index == @num_bytes
length = 1
else
length = Rubinius::Type.coerce_to length, Fixnum, :to_int
return if length < 0
end

return if index < 0 or index > @num_bytes
end

byteslice index, length
end

end
76 changes: 76 additions & 0 deletions truffle/src/main/ruby/core/rubinius/common/string.rb
Original file line number Diff line number Diff line change
@@ -47,6 +47,82 @@ def chomp(separator=$/)
str.chomp!(separator) || str
end

def each_line(sep=$/)
return to_enum(:each_line, sep) unless block_given?

# weird edge case.
if sep.nil?
yield self
return self
end

sep = StringValue(sep)

pos = 0

size = @num_bytes
orig_data = @data

# If the separator is empty, we're actually in paragraph mode. This
# is used so infrequently, we'll handle it completely separately from
# normal line breaking.
if sep.empty?
sep = "\n\n"
pat_size = 2

while pos < size
nxt = find_string(sep, pos)
break unless nxt

while @data[nxt] == 10 and nxt < @num_bytes
nxt += 1
end

match_size = nxt - pos

# string ends with \n's
break if pos == @num_bytes

str = byteslice pos, match_size
yield str unless str.empty?

# detect mutation within the block
if !@data.equal?(orig_data) or @num_bytes != size
raise RuntimeError, "string modified while iterating"
end

pos = nxt
end

# No more separates, but we need to grab the last part still.
fin = byteslice pos, @num_bytes - pos
yield fin if fin and !fin.empty?

else

# This is the normal case.
pat_size = sep.size
unmodified_self = clone

while pos < size
nxt = unmodified_self.find_string(sep, pos)
break unless nxt

match_size = nxt - pos
str = unmodified_self.byteslice pos, match_size + pat_size
yield str unless str.empty?

pos = nxt + pat_size
end

# No more separates, but we need to grab the last part still.
fin = unmodified_self.byteslice pos, @num_bytes - pos
yield fin unless fin.empty?
end

self
end

def lines(sep=$/)
if block_given?
each_line(sep) do |line|