Skip to content

Commit

Permalink
[Truffle] String#slice use :to_int for conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
Brandon Fish committed Sep 17, 2016
1 parent d08516b commit 41e28fd
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 20 deletions.
1 change: 0 additions & 1 deletion spec/truffle/tags/core/string/slice_tags.txt

This file was deleted.

Expand Up @@ -1199,6 +1199,46 @@ public long memhashLongLong(long a, long b) {



}

@Primitive(name = "fixnum_fits_into_int")
public static abstract class FixnumFitsIntoIntPrimitiveNode extends PrimitiveArrayArgumentsNode {

@Specialization
public boolean fitsIntoIntInt(int a) {
return true;
}

@Specialization
public boolean fitsIntoIntLong(long a) {
return CoreLibrary.fitsIntoInteger(a);
}

@Specialization(guards = "isRubyBignum(b)")
public boolean fitsIntoIntBignum(DynamicObject b) {
return false;
}

}

@Primitive(name = "fixnum_fits_into_long")
public static abstract class FixnumFitsIntoLongPrimitiveNode extends PrimitiveArrayArgumentsNode {

@Specialization
public boolean fitsIntoLongInt(int a) {
return true;
}

@Specialization
public boolean fitsIntoLongLong(long a) {
return true;
}

@Specialization(guards = "isRubyBignum(b)")
public boolean fitsIntoLongBignum(DynamicObject b) {
return false;
}

}

@Primitive(name = "fixnum_pow")
Expand Down
Expand Up @@ -503,7 +503,6 @@ public Object concat(
@CoreMethod(names = { "[]", "slice" }, required = 1, optional = 1, lowerFixnum = { 1, 2 }, taintFrom = 0)
public abstract static class GetIndexNode extends CoreMethodArrayArgumentsNode {

@Child private ToIntNode toIntNode;
@Child private CallDispatchHeadNode includeNode;
@Child private CallDispatchHeadNode dupNode;
@Child private StringSubstringPrimitiveNode substringNode;
Expand All @@ -527,8 +526,8 @@ public Object getIndex(VirtualFrame frame, DynamicObject string, int index, Obje
}

@Specialization(guards = { "!isRubyRange(index)", "!isRubyRegexp(index)", "!isRubyString(index)", "wasNotProvided(length) || isRubiniusUndefined(length)" })
public Object getIndex(VirtualFrame frame, DynamicObject string, Object index, Object length) {
return getIndex(frame, string, getToIntNode().doInt(frame, index), length);
public Object getIndex(VirtualFrame frame, DynamicObject string, Object index, Object length, @Cached("new()") SnippetNode snippetNode) {
return getIndex(frame, string, (int)snippetNode.execute(frame, "Rubinius::Type.rb_num2int(v)", "v", index), length);
}

@Specialization(guards = { "isIntRange(range)", "wasNotProvided(length) || isRubiniusUndefined(length)" })
Expand All @@ -543,10 +542,10 @@ public Object sliceLongRange(VirtualFrame frame, DynamicObject string, DynamicOb
}

@Specialization(guards = {"isObjectRange(range)", "wasNotProvided(length) || isRubiniusUndefined(length)"})
public Object sliceObjectRange(VirtualFrame frame, DynamicObject string, DynamicObject range, Object length) {
public Object sliceObjectRange(VirtualFrame frame, DynamicObject string, DynamicObject range, Object length, @Cached("new()") SnippetNode snippetNode1, @Cached("new()") SnippetNode snippetNode2) {
// TODO (nirvdrum 31-Mar-15) The begin and end values may return Fixnums beyond int boundaries and we should handle that -- Bignums are always errors.
final int coercedBegin = getToIntNode().doInt(frame, Layouts.OBJECT_RANGE.getBegin(range));
final int coercedEnd = getToIntNode().doInt(frame, Layouts.OBJECT_RANGE.getEnd(range));
final int coercedBegin = (int)snippetNode1.execute(frame, "Rubinius::Type.rb_num2int(v)", "v", Layouts.OBJECT_RANGE.getBegin(range));
final int coercedEnd = (int)snippetNode2.execute(frame, "Rubinius::Type.rb_num2int(v)", "v", Layouts.OBJECT_RANGE.getEnd(range));

return sliceRange(frame, string, coercedBegin, coercedEnd, Layouts.OBJECT_RANGE.getExcludedEnd(range));
}
Expand Down Expand Up @@ -591,13 +590,13 @@ public Object slice(VirtualFrame frame, DynamicObject string, int start, int len
}

@Specialization(guards = "wasProvided(length)")
public Object slice(VirtualFrame frame, DynamicObject string, int start, Object length) {
return slice(frame, string, start, getToIntNode().doInt(frame, length));
public Object slice(VirtualFrame frame, DynamicObject string, int start, Object length, @Cached("new()") SnippetNode snippetNode) {
return slice(frame, string, start, (int)snippetNode.execute(frame, "Rubinius::Type.rb_num2int(v)", "v", length));
}

@Specialization(guards = { "!isRubyRange(start)", "!isRubyRegexp(start)", "!isRubyString(start)", "wasProvided(length)" })
public Object slice(VirtualFrame frame, DynamicObject string, Object start, Object length) {
return slice(frame, string, getToIntNode().doInt(frame, start), getToIntNode().doInt(frame, length));
public Object slice(VirtualFrame frame, DynamicObject string, Object start, Object length, @Cached("new()") SnippetNode snippetNode1, @Cached("new()") SnippetNode snippetNode2) {
return slice(frame, string, (int)snippetNode1.execute(frame, "Rubinius::Type.rb_num2int(v)", "v", start), (int)snippetNode2.execute(frame, "Rubinius::Type.rb_num2int(v)", "v", length));
}

@Specialization(guards = {"isRubyRegexp(regexp)", "wasNotProvided(capture) || isRubiniusUndefined(capture)"})
Expand Down Expand Up @@ -654,15 +653,6 @@ public Object slice2(VirtualFrame frame, DynamicObject string, DynamicObject mat
return nil();
}

private ToIntNode getToIntNode() {
if (toIntNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toIntNode = insert(ToIntNode.create());
}

return toIntNode;
}

private StringSubstringPrimitiveNode getSubstringNode() {
if (substringNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
Expand Down
52 changes: 52 additions & 0 deletions truffle/src/main/ruby/core/type.rb
Expand Up @@ -220,6 +220,58 @@ def self.execute_check_convert_type(obj, cls, meth)
raise TypeError, msg
end

def self.rb_num2int(val)
num = rb_num2long(val)
check_int(num)
num
end

def self.rb_num2long(val)
raise TypeError, "no implicit conversion from nil to integer" if val.nil?

if object_kind_of?(val, Fixnum)
return val
elsif object_kind_of?(val, Float)
fval = val.to_int
check_long(fval)
return fval
elsif object_kind_of?(val, Bignum)
raise TypeError, "rb_num2long Bignum conversion not yet implemented"
else
return rb_num2long(rb_to_int(val))
end
end

def self.rb_to_int(val)
rb_to_integer(val, :to_int);
end

def self.rb_to_integer(val, meth)
return val if object_kind_of?(val, Fixnum) || object_kind_of?(val, Bignum)

This comment has been minimized.

Copy link
@eregon

eregon Sep 17, 2016

Member

This could be just kind_of?(Integer)

This comment has been minimized.

Copy link
@bjfish

bjfish Sep 20, 2016

Contributor

Thanks, I've updated this at 2e7a90c

res = convert_type(val, Integer, meth, true)
unless object_kind_of?(res, Integer)
conversion_mismatch(val, Integer, meth, res)
end
res
end

def self.conversion_mismatch(val, cls, meth, res)
raise TypeError, "can't convert #{val.class} to #{cls} (#{val.class}##{meth} gives #{res.class})"
end

def self.check_int(val)
unless Truffle.invoke_primitive(:fixnum_fits_into_int, val)
raise RangeError, "integer #{val} too #{val < 0 ? 'small' : 'big'} to convert to `int"
end
end

def self.check_long(val)
unless Truffle.invoke_primitive(:fixnum_fits_into_long, val)
raise RangeError, "integer #{val} too #{val < 0 ? 'small' : 'big'} to convert to `long"
end
end


This comment has been minimized.

Copy link
@chrisseaton

chrisseaton Sep 19, 2016

Contributor

Double blank line here.

def self.rb_check_convert_type(obj, cls, meth)
return obj if object_kind_of?(obj, cls)
v = convert_type(obj, cls, meth, false)
Expand Down

2 comments on commit 41e28fd

@bjfish
Copy link
Contributor

@bjfish bjfish commented on 41e28fd Sep 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chrisseaton Is this something you could reuse for the C API stuff?

I'm not sure if it matters to the C-API that we do the fixnum lowering (long -> int).

Please review when you can if there are any concerns: @nirvdrum @eregon

@chrisseaton
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, potentially. Our current logic for these conversions in cexts isn't well thought through - there are particularly some signed/unsigned things which are making me scratch my head.

Please sign in to comment.