Skip to content

Commit

Permalink
[Truffle] Initial implementation of String#insert.
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvdrum committed Jan 2, 2015
1 parent 3b95e2b commit 9d8cf7f
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 5 deletions.
64 changes: 64 additions & 0 deletions core/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java
Expand Up @@ -20,6 +20,7 @@
import org.joni.Matcher;
import org.joni.Option;
import org.joni.Region;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.runtime.Visibility;
Expand Down Expand Up @@ -809,6 +810,69 @@ public Object initializeCopy(RubyString self, RubyString from) {

}

@CoreMethod(names = "insert", required = 2, lowerFixnumParameters = 0)
public abstract static class InsertNode extends CoreMethodNode {

@Child protected ConcatNode concatNode;
@Child protected GetIndexNode getIndexNode;

public InsertNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
concatNode = StringNodesFactory.ConcatNodeFactory.create(context, sourceSection, new RubyNode[]{});
getIndexNode = StringNodesFactory.GetIndexNodeFactory.create(context, sourceSection, new RubyNode[]{});
}

public InsertNode(InsertNode prev) {
super(prev);
concatNode = prev.concatNode;
getIndexNode = prev.getIndexNode;
}

@Specialization
public RubyString insert(RubyString string, int index, RubyString otherString) {
notDesignedForCompilation();

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

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

if (index == -1) {
concatNode.concat(string, otherString);

return string;

} else if (index < 0) {
// Incrementing first seems weird, but MRI does it and it's significant because it uses the modified
// index value in its error messages. This seems wrong, but we should be compatible.
index++;

if (-index > string.length()) {
CompilerDirectives.transferToInterpreter();

throw new RaiseException(getContext().getCoreLibrary().indexError(String.format("index %d out of string", index), this));
}

index = index + string.length();

} else if (index > string.length()) {
CompilerDirectives.transferToInterpreter();

throw new RaiseException(getContext().getCoreLibrary().indexError(String.format("index %d out of string", index), this));
}

RubyString firstPart = getIndexNode.getIndex(string, 0, index);
RubyString secondPart = getIndexNode.getIndex(string, index, string.length());

RubyString concatenated = concatNode.concat(concatNode.concat(firstPart, otherString), secondPart);

string.set(concatenated.getBytes());

return string;
}
}

@CoreMethod(names = "ljust", required = 1, optional = 1, lowerFixnumParameters = 0)
public abstract static class LjustNode extends CoreMethodNode {

Expand Down
5 changes: 0 additions & 5 deletions spec/truffle/tags/core/string/insert_tags.txt
@@ -1,12 +1,7 @@
fails:String#insert with index, other inserts other before the character at the given index
fails:String#insert with index, other modifies self in place
fails:String#insert with index, other inserts after the given character on an negative count
fails:String#insert with index, other raises an IndexError if the index is beyond string
fails:String#insert with index, other converts index to an integer using to_int
fails:String#insert with index, other converts other to a string using to_str
fails:String#insert with index, other taints self if string to insert is tainted
fails:String#insert with index, other raises a TypeError if other can't be converted to string
fails:String#insert with index, other raises a RuntimeError if self is frozen
fails:String#insert with index, other inserts a character into a multibyte encoded string
fails:String#insert with index, other returns a String in the compatible encoding
fails:String#insert with index, other raises an Encoding::CompatibilityError if the encodings are incompatible

1 comment on commit 9d8cf7f

@chrisseaton
Copy link
Contributor

Choose a reason for hiding this comment

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

In both of the cases where you execute the child nodes, it's really important to understand that you're bypassing the DSL here. You're directly calling a specialisation. That looks fine in this case, but it isn't always. For an example using the DSL see for example https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java#L371. Here we define an abstract method that the DSL fills in for us as an entry point to the specialisation.

Please sign in to comment.