Skip to content

Commit

Permalink
[Truffle] Completed String#chr.
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvdrum committed Mar 22, 2015
1 parent ed2f7e0 commit 09bfd43
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 28 deletions.
2 changes: 0 additions & 2 deletions spec/truffle/tags/core/string/chr_tags.txt

This file was deleted.

Expand Up @@ -2350,32 +2350,6 @@ public RubyString clear(RubyString string) {
}
}

@CoreMethod(names = "chr")
public abstract static class ChrNode extends CoreMethodNode {

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

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

@Specialization
public RubyString chr(RubyString string) {
notDesignedForCompilation();
if (string.toString().isEmpty()) {
return string;
} else {
String head = string.toString().substring(0, 1);
ByteList byteString = ByteList.create(head);
byteString.setEncoding(string.getBytes().getEncoding());

return string.getContext().makeString(byteString);
}
}
}

public static class StringNodesHelper {

@TruffleBoundary
Expand Down
Expand Up @@ -61,6 +61,7 @@
import org.jcodings.Encoding;
import org.jcodings.exception.EncodingException;
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.cast.TaintResultNode;
import org.jruby.truffle.nodes.core.StringGuards;
Expand Down Expand Up @@ -851,4 +852,142 @@ public RubyString stringByteAppend(RubyString string, RubyString other) {

}

@RubiniusPrimitive(name = "string_substring")
@ImportGuards(StringGuards.class)
public static abstract class StringSubstringPrimitiveNode extends RubiniusPrimitiveNode {

@Child private TaintResultNode taintResultNode;

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

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

@Specialization(guards = "isSingleByteOptimizable")
public Object stringSubstringSingleByteOptimizable(RubyString string, int beg, int len) {
// Taken from org.jruby.RubyString#substr19.

if (len < 0) {
return nil();
}

final int length = string.getByteList().getRealSize();
if (length == 0) {
len = 0;
}

if (beg > length) {
return nil();
}

if (beg < 0) {
beg += length;

if (beg < 0) {
return nil();
}
}

if ((beg + len) > length) {
len = length - beg;
}

if (len <= 0) {
len = 0;
beg = 0;
}

return makeSubstring(string, beg, len);
}

@Specialization(guards = "!isSingleByteOptimizable")
public Object stringSubstring(RubyString string, int beg, int len) {
// Taken from org.jruby.RubyString#substr19 & org.jruby.RubyString#multibyteSubstr19.

if (len < 0) {
return nil();
}

final int length = string.getByteList().getRealSize();
if (length == 0) {
len = 0;
}

if ((beg + len) > length) {
len = length - beg;
}

final ByteList value = string.getByteList();
final Encoding enc = value.getEncoding();
int p;
int s = value.getBegin();
int end = s + length;
byte[]bytes = value.getUnsafeBytes();

if (beg < 0) {
if (len > -beg) len = -beg;
if (-beg * enc.maxLength() < length >>> 3) {
beg = -beg;
int e = end;
while (beg-- > len && (e = enc.prevCharHead(bytes, s, e, e)) != -1) {} // nothing
p = e;
if (p == -1) {
return nil();
}
while (len-- > 0 && (p = enc.prevCharHead(bytes, s, p, e)) != -1) {} // nothing
if (p == -1) {
return nil();
}
return makeSubstring(string, p - s, e - p);
} else {
beg += StringSupport.strLengthFromRubyString(string, enc);
if (beg < 0) {
return nil();
}
}
} else if (beg > 0 && beg > StringSupport.strLengthFromRubyString(string, enc)) {
return nil();
}
if (len == 0) {
p = 0;
} else if (string.isCodeRangeValid() && enc instanceof UTF8Encoding) {
p = StringSupport.utf8Nth(bytes, s, end, beg);
len = StringSupport.utf8Offset(bytes, p, end, len);
} else if (enc.isFixedWidth()) {
int w = enc.maxLength();
p = s + beg * w;
if (p > end) {
p = end;
len = 0;
} else if (len * w > end - p) {
len = end - p;
} else {
len *= w;
}
} else if ((p = StringSupport.nth(enc, bytes, s, end, beg)) == end) {
len = 0;
} else {
len = StringSupport.offset(enc, bytes, p, end, len);
}
return makeSubstring(string, p - s, len);
}

private RubyString makeSubstring(RubyString string, int beg, int len) {
if (taintResultNode == null) {
CompilerDirectives.transferToInterpreter();
taintResultNode = insert(new TaintResultNode(getContext(), getSourceSection(), true, new int[]{}));
}

final RubyString ret = getContext().makeString(string.getLogicalClass(), new ByteList(string.getByteList(), beg, len));
ret.getByteList().setEncoding(string.getByteList().getEncoding());
taintResultNode.maybeTaint(string, ret);

return ret;
}

}

}
5 changes: 5 additions & 0 deletions truffle/src/main/ruby/core/rubinius/bootstrap/string.rb
Expand Up @@ -38,6 +38,11 @@ def self.pattern(size, str)
raise PrimitiveFailure, "String.pattern primitive failed"
end

def substring(start, count)
Rubinius.primitive :string_substring
raise PrimitiveFailure, "String#substring primitive failed"
end

def find_string(pattern, start)
Rubinius.primitive :string_index
raise PrimitiveFailure, "String#find_string primitive failed"
Expand Down
4 changes: 4 additions & 0 deletions truffle/src/main/ruby/core/rubinius/common/string.rb
Expand Up @@ -202,6 +202,10 @@ def to_i(base=10)
to_inum(base, false)
end

def chr
substring 0, 1
end

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

Expand Down

0 comments on commit 09bfd43

Please sign in to comment.