Skip to content

Commit

Permalink
Showing 15 changed files with 358 additions and 313 deletions.
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@
import org.jruby.truffle.language.locals.WriteFrameSlotNode;
import org.jruby.truffle.language.locals.WriteFrameSlotNodeGen;
import org.jruby.truffle.language.objects.AllocateObjectNode;
import org.jruby.truffle.language.parser.jruby.Translator;
import org.jruby.truffle.language.threadlocal.ThreadLocalObject;

import java.util.ArrayList;
@@ -309,7 +310,9 @@ public static DynamicObject listLocalVariables(RubyContext context, Frame frame)
final List<Object> names = new ArrayList<>();
while (frame != null) {
for (FrameSlot slot : frame.getFrameDescriptor().getSlots()) {
if (slot.getIdentifier() instanceof String && !((String) slot.getIdentifier()).startsWith("rubytruffle_temp_frame_on_stack_marker")) {
if (slot.getIdentifier() instanceof String &&
!((String) slot.getIdentifier()).startsWith("rubytruffle_temp_frame_on_stack_marker") &&
!Translator.FRAME_LOCAL_GLOBAL_VARIABLES.contains(slot.getIdentifier())) {
names.add(context.getSymbolTable().getSymbol((String) slot.getIdentifier()));
}
}
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@
import org.jruby.truffle.core.cast.ToIntNode;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.string.StringGuards;
import org.jruby.truffle.core.string.StringNodesFactory;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.NotProvided;
import org.jruby.truffle.language.RubyGuards;
@@ -174,6 +175,12 @@ public abstract static class GetIndexNode extends CoreMethodArrayArgumentsNode {

@Child private ToIntNode toIntNode;

public static GetIndexNode create() {
return MatchDataNodesFactory.GetIndexNodeFactory.create(null);
}

public abstract Object executeGetIndex(VirtualFrame frame, Object matchData, Object index, Object length);

@Specialization
public Object getIndex(DynamicObject matchData, int index, NotProvided length,
@Cached("createBinaryProfile()") ConditionProfile indexOutOfBoundsProfile) {
131 changes: 109 additions & 22 deletions truffle/src/main/java/org/jruby/truffle/core/regexp/RegexpNodes.java
Original file line number Diff line number Diff line change
@@ -20,12 +20,15 @@

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameSlotTypeException;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
@@ -57,15 +60,18 @@
import org.jruby.truffle.core.rope.RopeNodesFactory;
import org.jruby.truffle.core.rope.RopeOperations;
import org.jruby.truffle.core.rubinius.RegexpPrimitiveNodes.RegexpSetLastMatchPrimitiveNode;
import org.jruby.truffle.core.rubinius.RegexpPrimitiveNodesFactory;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.core.thread.ThreadManager.BlockingAction;
import org.jruby.truffle.language.NotProvided;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.arguments.RubyArguments;
import org.jruby.truffle.language.control.RaiseException;
import org.jruby.truffle.language.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.language.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.language.objects.AllocateObjectNode;
import org.jruby.truffle.language.threadlocal.ThreadLocalObject;
import org.jruby.truffle.util.StringUtils;
import org.jruby.util.ByteList;
import org.jruby.util.RegexpOptions;
@@ -100,15 +106,15 @@ public static Matcher createMatcher(RubyContext context, DynamicObject regexp, D
}

@TruffleBoundary
public static Object matchCommon(RubyContext context, Node currentNode, RopeNodes.MakeSubstringNode makeSubstringNode, DynamicObject regexp, DynamicObject string, boolean operator,
public static DynamicObject matchCommon(RubyContext context, Node currentNode, RopeNodes.MakeSubstringNode makeSubstringNode, DynamicObject regexp, DynamicObject string,
boolean setNamedCaptures, int startPos) {
final Matcher matcher = createMatcher(context, regexp, string);
int range = StringOperations.rope(string).byteLength();
return matchCommon(context, currentNode, makeSubstringNode, regexp, string, operator, setNamedCaptures, matcher, startPos, range);
return matchCommon(context, currentNode, makeSubstringNode, regexp, string, setNamedCaptures, matcher, startPos, range);
}

@TruffleBoundary
public static Object matchCommon(RubyContext context, Node currentNode, RopeNodes.MakeSubstringNode makeSubstringNode, DynamicObject regexp, DynamicObject string, boolean operator,
public static DynamicObject matchCommon(RubyContext context, Node currentNode, RopeNodes.MakeSubstringNode makeSubstringNode, DynamicObject regexp, DynamicObject string,
boolean setNamedCaptures, Matcher matcher, int startPos, int range) {
assert RubyGuards.isRubyRegexp(regexp);
assert RubyGuards.isRubyString(string);
@@ -125,8 +131,6 @@ public Integer block() throws InterruptedException {
final DynamicObject nil = context.getCoreLibrary().getNilObject();

if (match == -1) {
RegexpSetLastMatchPrimitiveNode.setLastMatch(context, nil);

if (setNamedCaptures && Layouts.REGEXP.getRegex(regexp).numberOfNames() > 0) {
final Frame frame = context.getCallStack().getCallerFrameIgnoringSend().getFrame(FrameAccess.READ_WRITE, false);
for (Iterator<NameEntry> i = Layouts.REGEXP.getRegex(regexp).namedBackrefIterator(); i.hasNext();) {
@@ -162,8 +166,6 @@ public Integer block() throws InterruptedException {
final DynamicObject matchData = Layouts.MATCH_DATA.createMatchData(context.getCoreLibrary().getMatchDataFactory(),
string, regexp, region, values, pre, post, global, null);

RegexpSetLastMatchPrimitiveNode.setLastMatch(context, matchData);

if (setNamedCaptures && Layouts.REGEXP.getRegex(regexp).numberOfNames() > 0) {
final Frame frame = context.getCallStack().getCallerFrameIgnoringSend().getFrame(FrameAccess.READ_WRITE, false);
for (Iterator<NameEntry> i = Layouts.REGEXP.getRegex(regexp).namedBackrefIterator(); i.hasNext();) {
@@ -191,11 +193,7 @@ public Integer block() throws InterruptedException {
}
}

if (operator) {
return matcher.getBegin();
} else {
return matchData;
}
return matchData;
}

// because the factory is not constant
@@ -391,32 +389,38 @@ public static DynamicObject createRubyRegexp(DynamicObjectFactory factory, Regex
@CoreMethod(names = "=~", required = 1)
public abstract static class MatchOperatorNode extends CoreMethodArrayArgumentsNode {

@Child private CallDispatchHeadNode dupNode;
@Child private RopeNodes.MakeSubstringNode makeSubstringNode;
@Child private RegexpSetLastMatchPrimitiveNode setLastMatchNode;
@Child private CallDispatchHeadNode toSNode;
@Child private ToStrNode toStrNode;

public MatchOperatorNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
dupNode = DispatchHeadNodeFactory.createMethodCall(context);
makeSubstringNode = RopeNodesFactory.MakeSubstringNodeGen.create(null, null, null);
setLastMatchNode = RegexpPrimitiveNodesFactory.RegexpSetLastMatchPrimitiveNodeFactory.create(null);
}

@Specialization(guards = "isRubyString(string)")
public Object match(DynamicObject regexp, DynamicObject string) {
return matchCommon(getContext(), this, makeSubstringNode, regexp, string, true, true, 0);
public Object matchString(VirtualFrame frame, DynamicObject regexp, DynamicObject string) {
final DynamicObject dupedString = (DynamicObject) dupNode.call(frame, string, "dup");

return matchWithStringCopy(regexp, dupedString);
}

@Specialization(guards = "isRubySymbol(symbol)")
public Object match(VirtualFrame frame, DynamicObject regexp, DynamicObject symbol) {
public Object matchSymbol(VirtualFrame frame, DynamicObject regexp, DynamicObject symbol) {
if (toSNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toSNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

return match(regexp, (DynamicObject) toSNode.call(frame, symbol, "to_s"));
return matchWithStringCopy(regexp, (DynamicObject) toSNode.call(frame, symbol, "to_s"));
}

@Specialization(guards = "isNil(nil)")
public Object match(DynamicObject regexp, Object nil) {
public Object matchNil(DynamicObject regexp, Object nil) {
return nil();
}

@@ -427,7 +431,32 @@ public Object matchGeneric(VirtualFrame frame, DynamicObject regexp, DynamicObje
toStrNode = insert(ToStrNodeGen.create(getContext(), null, null));
}

return match(regexp, toStrNode.executeToStr(frame, other));
return matchWithStringCopy(regexp, toStrNode.executeToStr(frame, other));
}

// Creating a MatchData will store a copy of the source string. It's tempting to use a rope here, but a bit
// inconvenient because we can't work with ropes directly in Ruby and some MatchData methods are nicely
// implemented using the source string data. Likewise, we need to taint objects based on the source string's
// taint state. We mustn't allow the source string's contents to change, however, so we must ensure that we have
// a private copy of that string. Since the source string would otherwise be a reference to string held outside
// the MatchData object, it would be possible for the source string to be modified externally.
//
// Ex. x = "abc"; x =~ /(.*)/; x.upcase!
//
// Without a private copy, the MatchData's source could be modified to be upcased when it should remain the
// same as when the MatchData was created.
private Object matchWithStringCopy(DynamicObject regexp, DynamicObject string) {
final Matcher matcher = createMatcher(getContext(), regexp, string);
final int range = StringOperations.rope(string).byteLength();

final DynamicObject matchData = matchCommon(getContext(), this, makeSubstringNode, regexp, string, true, matcher, 0, range);
setLastMatchNode.executeSetLastMatch(matchData);

if (matchData != nil()) {
return matcher.getBegin();
}

return nil();
}

}
@@ -447,24 +476,78 @@ public int hash(DynamicObject regexp) {
@CoreMethod(names = "match_start", required = 2)
public abstract static class MatchStartNode extends CoreMethodArrayArgumentsNode {

@Child private CallDispatchHeadNode dupNode;
@Child private RopeNodes.MakeSubstringNode makeSubstringNode;

public MatchStartNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
dupNode = DispatchHeadNodeFactory.createMethodCall(context);
makeSubstringNode = RopeNodesFactory.MakeSubstringNodeGen.create(null, null, null);
}

@Specialization(guards = "isRubyString(string)")
public Object matchStart(DynamicObject regexp, DynamicObject string, int startPos) {
final Object matchResult = matchCommon(getContext(), this, makeSubstringNode, regexp, string, false, false, startPos);
public Object matchStart(VirtualFrame frame, DynamicObject regexp, DynamicObject string, int startPos) {
final DynamicObject dupedString = (DynamicObject) dupNode.call(frame, string, "dup");
final Object matchResult = matchCommon(getContext(), this, makeSubstringNode, regexp, dupedString, false, startPos);

if (RubyGuards.isRubyMatchData(matchResult) && Layouts.MATCH_DATA.getRegion((DynamicObject) matchResult).numRegs > 0
&& Layouts.MATCH_DATA.getRegion((DynamicObject) matchResult).beg[0] == startPos) {
return matchResult;
}

return nil();
}
}

@CoreMethod(names = "last_match", onSingleton = true, optional = 1, lowerFixnum = 1)
public abstract static class LastMatchNode extends CoreMethodArrayArgumentsNode {

@Specialization
public Object lastMatch(NotProvided index) {
return getMatchData();
}

@Specialization
public Object lastMatch(VirtualFrame frame, int index,
@Cached("create()") MatchDataNodes.GetIndexNode getIndexNode) {
final Object matchData = getMatchData();

if (matchData == nil()) {
return nil();
} else {
return getIndexNode.executeGetIndex(frame, matchData, index, NotProvided.INSTANCE);
}
}

@TruffleBoundary
private Object getMatchData() {
Frame frame = getContext().getCallStack().getCallerFrameIgnoringSend().getFrame(FrameAccess.READ_ONLY, true);
FrameSlot slot = frame.getFrameDescriptor().findFrameSlot("$~");

while (slot == null) {
final Frame nextFrame = RubyArguments.getDeclarationFrame(frame);

if (nextFrame == null) {
return nil();
} else {
slot = nextFrame.getFrameDescriptor().findFrameSlot("$~");
frame = nextFrame;
}
}

final Object previousMatchData = frame.getValue(slot);

if (previousMatchData instanceof ThreadLocalObject) {
final ThreadLocalObject threadLocalObject = (ThreadLocalObject) previousMatchData;

return threadLocalObject.get();
} else {
return previousMatchData;
}
}

}

@CoreMethod(names = { "quote", "escape" }, onSingleton = true, required = 1)
public abstract static class QuoteNode extends CoreMethodArrayArgumentsNode {

@@ -501,16 +584,20 @@ public DynamicObject quote(VirtualFrame frame, Object raw) {
@CoreMethod(names = "search_from", required = 2)
public abstract static class SearchFromNode extends CoreMethodArrayArgumentsNode {

@Child private CallDispatchHeadNode dupNode;
@Child private RopeNodes.MakeSubstringNode makeSubstringNode;

public SearchFromNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
dupNode = DispatchHeadNodeFactory.createMethodCall(context);
makeSubstringNode = RopeNodesFactory.MakeSubstringNodeGen.create(null, null, null);
}

@Specialization(guards = "isRubyString(string)")
public Object searchFrom(DynamicObject regexp, DynamicObject string, int startPos) {
return matchCommon(getContext(), this, makeSubstringNode, regexp, string, false, false, startPos);
public Object searchFrom(VirtualFrame frame, DynamicObject regexp, DynamicObject string, int startPos) {
final DynamicObject dupedString = (DynamicObject) dupNode.call(frame, string, "dup");

return matchCommon(getContext(), this, makeSubstringNode, regexp, dupedString, false, startPos);
}
}

Original file line number Diff line number Diff line change
@@ -731,7 +731,7 @@ public Rope withEncodingSameCodeRange(Rope rope, Encoding encoding, CodeRange co
@Specialization(guards = {
"rope.getEncoding() != encoding",
"rope.getCodeRange() != codeRange",
"isAsciiCompatbileChange(rope, encoding)",
"isAsciiCompatibleChange(rope, encoding)",
"rope.getClass() == cachedRopeClass"
}, limit = "getCacheLimit()")
public Rope withEncodingCr7Bit(Rope rope, Encoding encoding, CodeRange codeRange,
@@ -742,14 +742,14 @@ public Rope withEncodingCr7Bit(Rope rope, Encoding encoding, CodeRange codeRange
@Specialization(guards = {
"rope.getEncoding() != encoding",
"rope.getCodeRange() != codeRange",
"!isAsciiCompatbileChange(rope, encoding)"
"!isAsciiCompatibleChange(rope, encoding)"
})
public Rope withEncoding(Rope rope, Encoding encoding, CodeRange codeRange,
@Cached("create()") MakeLeafRopeNode makeLeafRopeNode) {
return makeLeafRopeNode.executeMake(rope.getBytes(), encoding, codeRange, NotProvided.INSTANCE);
}

protected static boolean isAsciiCompatbileChange(Rope rope, Encoding encoding) {
protected static boolean isAsciiCompatibleChange(Rope rope, Encoding encoding) {
return rope.getCodeRange() == CR_7BIT && encoding.isAsciiCompatible();
}

Loading

0 comments on commit f1b2a0e

Please sign in to comment.