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();
}

Original file line number Diff line number Diff line change
@@ -13,6 +13,13 @@
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.frame.FrameSlotTypeException;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.joni.Matcher;
import org.jruby.truffle.Layouts;
@@ -23,7 +30,11 @@
import org.jruby.truffle.core.regexp.RegexpNodes;
import org.jruby.truffle.core.rope.RopeNodes;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.RubyGuards;
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.threadlocal.ThreadLocalObject;
import org.jruby.truffle.util.StringUtils;

/**
@@ -32,6 +43,10 @@
*/
public abstract class RegexpPrimitiveNodes {

public static boolean isSuitableMatchDataType(RubyContext context, DynamicObject matchData) {
return matchData == context.getCoreLibrary().getNilObject() || RubyGuards.isRubyMatchData(matchData);
}

@Primitive(name = "regexp_fixed_encoding_p")
public static abstract class RegexpFixedEncodingPrimitiveNode extends PrimitiveArrayArgumentsNode {

@@ -80,17 +95,6 @@ public int optionsNotInitialized(DynamicObject regexp) {

}

@Primitive(name = "regexp_propagate_last_match")
public static abstract class RegexpPropagateLastMatchPrimitiveNode extends PrimitiveArrayArgumentsNode {

@Specialization
public DynamicObject propagateLastMatch(DynamicObject regexpClass) {
// TODO (nirvdrum 08-Jun-15): This method seems to exist just to fix Rubinius's broken frame-local scoping. This assertion needs to be verified, however.
return nil();
}

}

@Primitive(name = "regexp_search_region", lowerFixnum = { 2, 3 })
@ImportStatic(RegexpGuards.class)
public static abstract class RegexpSearchRegionPrimitiveNode extends PrimitiveArrayArgumentsNode {
@@ -110,48 +114,114 @@ private String formatError(DynamicObject string) {
return StringUtils.format("invalid byte sequence in %s", Layouts.STRING.getRope(string).getEncoding());
}

@TruffleBoundary
@Specialization(guards = {"isInitialized(regexp)", "isRubyString(string)", "isValidEncoding(string)"})
public Object searchRegion(DynamicObject regexp, DynamicObject string, int start, int end, boolean forward,
@Cached("createX()") RopeNodes.MakeSubstringNode makeSubstringNode) {
final Matcher matcher = RegexpNodes.createMatcher(getContext(), regexp, string);
public Object searchRegion(VirtualFrame frame, DynamicObject regexp, DynamicObject string,
int start, int end, boolean forward,
@Cached("createX()") RopeNodes.MakeSubstringNode makeSubstringNode,
@Cached("createMethodCall()") CallDispatchHeadNode dupNode) {
final DynamicObject dupedString = (DynamicObject) dupNode.call(frame, string, "dup");
final Matcher matcher = RegexpNodes.createMatcher(getContext(), regexp, dupedString);

if (forward) {
// Search forward through the string.
return RegexpNodes.matchCommon(getContext(), this, makeSubstringNode, regexp, string, false, false, matcher, start, end);
return RegexpNodes.matchCommon(getContext(), this, makeSubstringNode, regexp, dupedString, false, matcher, start, end);
} else {
// Search backward through the string.
return RegexpNodes.matchCommon(getContext(), this, makeSubstringNode, regexp, string, false, false, matcher, end, start);
return RegexpNodes.matchCommon(getContext(), this, makeSubstringNode, regexp, dupedString, false, matcher, end, start);
}
}

}

@Primitive(name = "regexp_set_last_match")
@Primitive(name = "regexp_set_last_match", needsSelf = false)
@ImportStatic(RegexpPrimitiveNodes.class)
public static abstract class RegexpSetLastMatchPrimitiveNode extends PrimitiveArrayArgumentsNode {

@Specialization
public Object setLastMatchData(DynamicObject regexpClass, Object matchData) {
setLastMatch(getContext(), matchData);
return matchData;
public static RegexpSetLastMatchPrimitiveNode create() {
return RegexpPrimitiveNodesFactory.RegexpSetLastMatchPrimitiveNodeFactory.create(null);
}

public abstract DynamicObject executeSetLastMatch(Object matchData);

@TruffleBoundary
public static void setLastMatch(RubyContext context, Object matchData) {
final DynamicObject threadLocals = Layouts.THREAD.getThreadLocals(context.getThreadManager().getCurrentThread());
boolean res = threadLocals.set("$~", matchData);
assert res;
@Specialization(guards = "isSuitableMatchDataType(getContext(), matchData)")
public DynamicObject setLastMatchData(DynamicObject matchData) {
Frame frame = getContext().getCallStack().getCallerFrameIgnoringSend().getFrame(FrameInstance.FrameAccess.READ_WRITE, true);
FrameSlot slot = frame.getFrameDescriptor().findFrameSlot("$~");

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

if (nextFrame == null) {
slot = frame.getFrameDescriptor().addFrameSlot("$~", FrameSlotKind.Object);
} else {
slot = nextFrame.getFrameDescriptor().findFrameSlot("$~");
frame = nextFrame;
}
}

final Object previousMatchData;
try {
previousMatchData = frame.getObject(slot);

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

threadLocalObject.set(matchData);
} else {
frame.setObject(slot, ThreadLocalObject.wrap(getContext(), matchData));
}
} catch (FrameSlotTypeException e) {
throw new IllegalStateException(e);
}

return matchData;
}

}

@Primitive(name = "regexp_set_block_last_match")
@Primitive(name = "regexp_set_block_last_match", needsSelf = false)
@ImportStatic(RegexpPrimitiveNodes.class)
public static abstract class RegexpSetBlockLastMatchPrimitiveNode extends PrimitiveArrayArgumentsNode {

@Specialization
public DynamicObject setBlockLastMatch(DynamicObject regexpClass) {
// TODO CS 7-Mar-15 what does this do?
return nil();
@TruffleBoundary
@Specialization(guards = { "isRubyProc(block)", "isSuitableMatchDataType(getContext(), matchData)" })
public Object setBlockLastMatch(DynamicObject block, DynamicObject matchData) {

Frame methodFrame = Layouts.PROC.getDeclarationFrame(block);

if (methodFrame == null) {
return matchData;
}

Frame tempFrame = RubyArguments.getDeclarationFrame(methodFrame);

while (tempFrame != null) {
methodFrame = tempFrame;
tempFrame = RubyArguments.getDeclarationFrame(methodFrame);
}

final FrameDescriptor callerFrameDescriptor = methodFrame.getFrameDescriptor();

try {
final FrameSlot frameSlot = callerFrameDescriptor.findFrameSlot("$~");

if (frameSlot == null) {
return matchData;
}

final Object matchDataHolder = methodFrame.getObject(frameSlot);

if (matchDataHolder instanceof ThreadLocalObject) {
((ThreadLocalObject) matchDataHolder).set(matchData);
} else {
methodFrame.setObject(frameSlot, ThreadLocalObject.wrap(getContext(), matchData));
}
} catch (FrameSlotTypeException e) {
throw new IllegalStateException(e);
}

return matchData;
}

}
Original file line number Diff line number Diff line change
@@ -126,6 +126,7 @@
import org.jruby.truffle.core.rope.RopeNodesFactory;
import org.jruby.truffle.core.rope.RopeOperations;
import org.jruby.truffle.core.rope.SubstringRope;
import org.jruby.truffle.core.rubinius.RegexpPrimitiveNodes;
import org.jruby.truffle.core.string.StringNodesFactory.StringAreComparableNodeGen;
import org.jruby.truffle.core.string.StringNodesFactory.StringEqualNodeGen;
import org.jruby.truffle.language.CheckLayoutNode;
@@ -578,8 +579,9 @@ public Object slice1(
DynamicObject string,
DynamicObject regexp,
Object capture,
@Cached("new()") SnippetNode snippetNode) {
return snippetNode.execute(frame, "match, str = subpattern(index, 0); Regexp.last_match = match; str", "index", regexp);
@Cached("createMethodCallIgnoreVisibility()") CallDispatchHeadNode callNode,
@Cached("create()") RegexpPrimitiveNodes.RegexpSetLastMatchPrimitiveNode setLastMatchNode) {
return sliceCapture(frame, string, regexp, 0, callNode, setLastMatchNode);
}

@Specialization(guards = {"isRubyRegexp(regexp)", "wasProvided(capture)"})
@@ -588,8 +590,20 @@ public Object sliceCapture(
DynamicObject string,
DynamicObject regexp,
Object capture,
@Cached("new()") SnippetNode snippetNode) {
return snippetNode.execute(frame, "match, str = subpattern(index, other); Regexp.last_match = match; str", "index", regexp, "other", capture);
@Cached("createMethodCallIgnoreVisibility()") CallDispatchHeadNode callNode,
@Cached("create()") RegexpPrimitiveNodes.RegexpSetLastMatchPrimitiveNode setLastMatchNode) {
final Object matchStrPair = callNode.call(frame, string, "subpattern", regexp, capture);

if (matchStrPair == nil()) {
setLastMatchNode.executeSetLastMatch(nil());
return nil();
}

final Object[] array = (Object[]) Layouts.ARRAY.getStore((DynamicObject) matchStrPair);

setLastMatchNode.executeSetLastMatch(array[0]);

return array[1];
}

@Specialization(guards = {"wasNotProvided(length) || isRubiniusUndefined(length)", "isRubyString(matchStr)"})
Original file line number Diff line number Diff line change
@@ -9,14 +9,14 @@
*/
package org.jruby.truffle.language.globals;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.threadlocal.GetFromThreadLocalNode;

public class ReadMatchReferenceNode extends RubyNode {

@@ -27,18 +27,23 @@ public class ReadMatchReferenceNode extends RubyNode {

private final int index;

@Child private ReadThreadLocalGlobalVariableNode readMatchNode;
@Child private GetFromThreadLocalNode readMatchNode;

private final ConditionProfile matchNilProfile = ConditionProfile.createBinaryProfile();

public ReadMatchReferenceNode(RubyContext context, SourceSection sourceSection, int index) {
public ReadMatchReferenceNode(RubyContext context, SourceSection sourceSection, GetFromThreadLocalNode readMatchNode, int index) {
super(context, sourceSection);
this.readMatchNode = readMatchNode;
this.index = index;
}

@Override
public Object execute(VirtualFrame frame) {
final Object match = getReadMatchNode().execute(frame);
if (readMatchNode == null) {
return nil();
}

final Object match = readMatchNode.execute(frame);

if (matchNilProfile.profile(match == nil())) {
return nil();
@@ -88,13 +93,4 @@ public Object isDefined(VirtualFrame frame) {
}
}

private ReadThreadLocalGlobalVariableNode getReadMatchNode() {
if (readMatchNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
readMatchNode = insert(new ReadThreadLocalGlobalVariableNode(getContext(), null, "$~", true));
}

return readMatchNode;
}

}
Original file line number Diff line number Diff line change
@@ -160,7 +160,7 @@
import org.jruby.truffle.language.objects.SingletonClassNodeGen;
import org.jruby.truffle.language.objects.WriteClassVariableNode;
import org.jruby.truffle.language.objects.WriteInstanceVariableNode;
import org.jruby.truffle.language.threadlocal.GetFromThreadLocalNodeGen;
import org.jruby.truffle.language.threadlocal.GetFromThreadLocalNode;
import org.jruby.truffle.language.threadlocal.ThreadLocalObjectNode;
import org.jruby.truffle.language.threadlocal.ThreadLocalObjectNodeGen;
import org.jruby.truffle.language.threadlocal.WrapInThreadLocalNodeGen;
@@ -207,7 +207,7 @@ public class BodyTranslator extends Translator {
protected boolean usesRubiniusPrimitive = false;

private static final Set<String> THREAD_LOCAL_GLOBAL_VARIABLES = new HashSet<>(
Arrays.asList("$~", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$!", "$?")); // "$_"
Arrays.asList("$!", "$?")); // "$_"

private static final Set<String> READ_ONLY_GLOBAL_VARIABLES = new HashSet<String>(
Arrays.asList("$:", "$LOAD_PATH", "$-I", "$\"", "$LOADED_FEATURES", "$<", "$FILENAME", "$?", "$-a", "$-l", "$-p", "$!"));
@@ -1580,6 +1580,9 @@ public RubyNode visitGlobalAsgnNode(org.jruby.ast.GlobalAsgnNode node) {

if (name.equals("$~")) {
rhs = new CheckMatchVariableTypeNode(context, sourceSection.toSourceSection(source), rhs);
rhs = WrapInThreadLocalNodeGen.create(context, sourceSection.toSourceSection(source), rhs);

environment.declareVarWhereAllowed("$~");
} else if (name.equals("$0")) {
rhs = new CheckProgramNameVariableTypeNode(context, sourceSection.toSourceSection(source), rhs);
} else if (name.equals("$/")) {
@@ -1641,9 +1644,9 @@ public RubyNode visitGlobalAsgnNode(org.jruby.ast.GlobalAsgnNode node) {

RubyNode assignment = localVarNode.makeWriteNode(rhs);

if (name.equals("$_")) {
if (name.equals("$_") || name.equals("$~")) {
// TODO CS 4-Jan-16 I can't work out why this is a *get* node
assignment = GetFromThreadLocalNodeGen.create(context, fullSourceSection, assignment);
assignment = new GetFromThreadLocalNode(context, fullSourceSection, assignment);
}

return addNewlineIfNeeded(node, assignment);
@@ -1685,10 +1688,14 @@ public RubyNode visitGlobalVarNode(org.jruby.ast.GlobalVarNode node) {
if (getSourcePath(sourceSection).equals(buildCorePath("regexp.rb"))) {
readNode = new RubiniusLastStringReadNode(context, fullSourceSection);
} else {
readNode = GetFromThreadLocalNodeGen.create(context, fullSourceSection, readNode);
readNode = new GetFromThreadLocalNode(context, fullSourceSection, readNode);
}
}

if (name.equals("$~")) {
readNode = new GetFromThreadLocalNode(context, fullSourceSection, readNode);
}

ret = readNode;
} else if (THREAD_LOCAL_GLOBAL_VARIABLES.contains(name)) {
ret = new ReadThreadLocalGlobalVariableNode(context, fullSourceSection, name, ALWAYS_DEFINED_GLOBALS.contains(name));
@@ -2426,7 +2433,14 @@ public RubyNode visitNilNode(org.jruby.ast.NilNode node) {

@Override
public RubyNode visitNthRefNode(org.jruby.ast.NthRefNode node) {
final RubyNode ret = new ReadMatchReferenceNode(context, translate(node.getPosition()).toSourceSection(source), node.getMatchNumber());
final RubySourceSection sourceSection = translate(node.getPosition());
final SourceSection fullSourceSection = sourceSection.toSourceSection(source);

environment.declareVarWhereAllowed("$~");

final GetFromThreadLocalNode readMatchNode = new GetFromThreadLocalNode(context, fullSourceSection, environment.findLocalVarNode("$~", source, sourceSection));
final RubyNode ret = new ReadMatchReferenceNode(context, fullSourceSection, readMatchNode, node.getMatchNumber());

return addNewlineIfNeeded(node, ret);
}

@@ -3055,7 +3069,14 @@ public RubyNode visitBackRefNode(org.jruby.ast.BackRefNode node) {
throw new UnsupportedOperationException(Character.toString(node.getType()));
}

final RubyNode ret = new ReadMatchReferenceNode(context, translate(node.getPosition()).toSourceSection(source), index);
final RubySourceSection sourceSection = translate(node.getPosition());
final SourceSection fullSourceSection = sourceSection.toSourceSection(source);

environment.declareVarWhereAllowed("$~");

final GetFromThreadLocalNode readMatchNode = new GetFromThreadLocalNode(context, fullSourceSection, environment.findLocalVarNode("$~", source, sourceSection));
final RubyNode ret = new ReadMatchReferenceNode(context, fullSourceSection, readMatchNode, index);

return addNewlineIfNeeded(node, ret);
}

Original file line number Diff line number Diff line change
@@ -33,8 +33,8 @@

public abstract class Translator extends org.jruby.ast.visitor.AbstractNodeVisitor<RubyNode> {

public static final Set<String> ALWAYS_DEFINED_GLOBALS = new HashSet<>(Arrays.asList("$~", "$!"));
public static final Set<String> FRAME_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$_", "$+", "$&", "$`", "$'"));
public static final Set<String> ALWAYS_DEFINED_GLOBALS = new HashSet<>(Arrays.asList("$!", "$~"));
public static final Set<String> FRAME_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$_", "$+", "$&", "$`", "$'", "$~", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9"));

protected final Node currentNode;
protected final RubyContext context;
Original file line number Diff line number Diff line change
@@ -12,27 +12,40 @@
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.RubyNode;

@NodeChild(value = "value", type = RubyNode.class)
public abstract class GetFromThreadLocalNode extends RubyNode {
public class GetFromThreadLocalNode extends RubyNode {

public GetFromThreadLocalNode(RubyContext context, SourceSection sourceSection) {
@Child private RubyNode value;

public GetFromThreadLocalNode(RubyContext context, SourceSection sourceSection, RubyNode value) {
super(context, sourceSection);
this.value = value;
}

@TruffleBoundary
@Specialization
public Object get(ThreadLocalObject threadLocal) {
return threadLocal.get();
@Override
public Object isDefined(VirtualFrame frame) {
return value.isDefined(frame);
}

@Specialization(guards = "!isThreadLocal(value)")
public Object get(Object value) {
return value;
@Override
public Object execute(VirtualFrame frame) {
final Object threadLocalObject = value.execute(frame);

if (RubyGuards.isThreadLocal(threadLocalObject)) {
return getThreadLocalValue((ThreadLocalObject) threadLocalObject);
}

return threadLocalObject;
}

@TruffleBoundary
private Object getThreadLocalValue(ThreadLocalObject threadLocalObject) {
return threadLocalObject.get();
}

}
7 changes: 4 additions & 3 deletions truffle/src/main/ruby/core/enumerable.rb
Original file line number Diff line number Diff line change
@@ -345,25 +345,26 @@ def each_with_index(*args)
self
end

def grep(pattern)
def grep(pattern, &block)
ary = []

if block_given?
each do
o = Truffle.single_block_arg
if pattern === o
Regexp.set_block_last_match
Regexp.set_block_last_match(block, $~)
ary << yield(o)
end
end
else
each do
o = Truffle.single_block_arg
if pattern === o
Regexp.set_block_last_match
ary << o
end
end

Truffle.invoke_primitive(:regexp_set_last_match, $~)
end

ary
13 changes: 9 additions & 4 deletions truffle/src/main/ruby/core/enumerator.rb
Original file line number Diff line number Diff line change
@@ -370,23 +370,28 @@ def reject
end
end

def grep(pattern)
def grep(pattern, &block)
if block_given?
Lazy.new(self, nil) do |yielder, *args|
val = args.length >= 2 ? args : args.first
if pattern === val
Regexp.set_block_last_match
Regexp.set_block_last_match(block, $~)
yielder.yield yield(val)
end
end
else
Lazy.new(self, nil) do |yielder, *args|
lazy = Lazy.new(self, nil) do |yielder, *args|
val = args.length >= 2 ? args : args.first
if pattern === val
Regexp.set_block_last_match
yielder.yield val
end

Truffle.invoke_primitive(:regexp_set_last_match, $~)
end

Truffle.invoke_primitive(:regexp_set_last_match, $~)

lazy
end
end

28 changes: 7 additions & 21 deletions truffle/src/main/ruby/core/regexp.rb
Original file line number Diff line number Diff line change
@@ -103,15 +103,6 @@ def options
raise PrimitiveFailure, "Regexp#options primitive failed"
end

def self.last_match(n = nil)
if n
# TODO (nirvdrum Jan. 8, 2015) Make sure this supports symbol keys for named capture lookup.
$~.values_at(n).first
else
$~
end
end

def self.last_match=(match)
Truffle.primitive :regexp_set_last_match

@@ -122,12 +113,7 @@ def self.last_match=(match)
raise PrimitiveFailure, "Regexp#set_last_match primitive failed"
end

def self.propagate_last_match
Truffle.primitive :regexp_propagate_last_match
raise PrimitiveFailure, "Regexp#propagate_last_match primitive failed"
end

def self.set_block_last_match
def self.set_block_last_match(block, match_data)
Truffle.primitive :regexp_set_block_last_match
raise PrimitiveFailure, "Regexp#set_block_last_match primitive failed"
end
@@ -222,7 +208,7 @@ def initialize_copy(other)

def match(str, pos=0)
unless str
Regexp.last_match = nil
Truffle.invoke_primitive(:regexp_set_last_match, nil)
return nil
end

@@ -233,7 +219,7 @@ def match(str, pos=0)
pos = pos < 0 ? pos + str.size : pos
pos = m.character_to_byte_index pos
result = search_region(str, pos, str.bytesize, true)
Regexp.last_match = result
Truffle.invoke_primitive(:regexp_set_last_match, result)

if result && block_given?
yield result
@@ -248,16 +234,16 @@ def ===(other)
elsif !other.kind_of? String
other = Rubinius::Type.check_convert_type other, String, :to_str
unless other
Regexp.last_match = nil
Truffle.invoke_primitive(:regexp_set_last_match, nil)
return false
end
end

if match = match_from(other, 0)
Regexp.last_match = match
Truffle.invoke_primitive(:regexp_set_last_match, match)
true
else
Regexp.last_match = nil
Truffle.invoke_primitive(:regexp_set_last_match, nil)
false
end
end
@@ -286,7 +272,7 @@ def ~
line = $_

unless line.kind_of?(String)
Regexp.last_match = nil
Truffle.invoke_primitive(:regexp_set_last_match, nil)
return nil
end

220 changes: 31 additions & 189 deletions truffle/src/main/ruby/core/string.rb
Original file line number Diff line number Diff line change
@@ -133,7 +133,7 @@ def =~(pattern)
case pattern
when Regexp
match_data = pattern.search_region(self, 0, bytesize, true)
Regexp.last_match = match_data
Truffle.invoke_primitive(:regexp_set_last_match, match_data)
return match_data.begin(0) if match_data
when String
raise TypeError, "type mismatch: String given"
@@ -191,7 +191,7 @@ def partition(pattern=nil)

if pattern.kind_of? Regexp
if m = pattern.match(self)
Regexp.last_match = m
Truffle.invoke_primitive(:regexp_set_last_match, m)
return [m.pre_match, m.to_s, m.post_match]
end
else
@@ -213,7 +213,7 @@ def partition(pattern=nil)
def rpartition(pattern)
if pattern.kind_of? Regexp
if m = pattern.search_region(self, 0, size, false)
Regexp.last_match = m
Truffle.invoke_primitive(:regexp_set_last_match, m)
[m.pre_match, m[0], m.post_match]
end
else
@@ -268,14 +268,14 @@ def scan(pattern)
val.taint if taint

if block_given?
Regexp.last_match = match
Truffle.invoke_primitive(:regexp_set_last_match, match)
yield(val)
else
ret << val
end
end

Regexp.last_match = last_match
Truffle.invoke_primitive(:regexp_set_last_match, last_match)
return ret
end

@@ -597,75 +597,14 @@ def upto(stop, exclusive=false)
self
end

def sub(pattern, replacement=undefined)
# Because of the behavior of $~, this is duplicated from sub! because
# if we call sub! from sub, the last_match can't be updated properly.
dup = self.dup

unless valid_encoding?
raise ArgumentError, "invalid byte sequence in #{encoding}"
end

if undefined.equal? replacement
unless block_given?
raise ArgumentError, "method '#{__method__}': given 1, expected 2"
end
use_yield = true
tainted = false
else
tainted = replacement.tainted?
untrusted = replacement.untrusted?

unless replacement.kind_of?(String)
hash = Rubinius::Type.check_convert_type(replacement, Hash, :to_hash)
replacement = StringValue(replacement) unless hash
tainted ||= replacement.tainted?
untrusted ||= replacement.untrusted?
end
use_yield = false
end

pattern = Rubinius::Type.coerce_to_regexp(pattern, true) unless pattern.kind_of? Regexp
match = pattern.match_from(dup, 0)

Regexp.last_match = match

ret = byteslice(0, 0) # Empty string and string subclass

if match
ret.append match.pre_match

if use_yield || hash
Regexp.last_match = match

if use_yield
val = yield match.to_s
else
val = hash[match.to_s]
end
untrusted = true if val.untrusted?
val = val.to_s unless val.kind_of?(String)

tainted ||= val.tainted?

ret.append val
else
replacement.to_sub_replacement(ret, match)
end

ret.append(match.post_match)
tainted ||= val.tainted?
else
return dup
end

ret.taint if tainted
ret.untrust if untrusted

ret
def sub(pattern, replacement=undefined, &block)
s = dup
s.sub!(pattern, replacement, &block)
Truffle.invoke_primitive(:regexp_set_last_match, $~)
s
end

def sub!(pattern, replacement=undefined)
def sub!(pattern, replacement=undefined, &block)
# Because of the behavior of $~, this is duplicated from sub! because
# if we call sub! from sub, the last_match can't be updated properly.

@@ -697,16 +636,15 @@ def sub!(pattern, replacement=undefined)
pattern = Rubinius::Type.coerce_to_regexp(pattern, true) unless pattern.kind_of? Regexp
match = pattern.match_from(self, 0)

Regexp.last_match = match
Regexp.set_block_last_match(block, match) if block_given?
Truffle.invoke_primitive(:regexp_set_last_match, match)

ret = byteslice(0, 0) # Empty string and string subclass

if match
ret.append match.pre_match

if use_yield || hash
Regexp.last_match = match

duped = dup
if use_yield
val = yield match.to_s
@@ -750,7 +688,7 @@ def slice!(one, two=undefined)
if one.kind_of? Regexp
lm = Regexp.last_match
self[one] = '' if result
Regexp.last_match = lm
Truffle.invoke_primitive(:regexp_set_last_match, lm)
else
self[one] = '' if result
end
@@ -760,7 +698,7 @@ def slice!(one, two=undefined)
if one.kind_of? Regexp
lm = Regexp.last_match
self[one, two] = '' if result
Regexp.last_match = lm
Truffle.invoke_primitive(:regexp_set_last_match, lm)
else
self[one, two] = '' if result
end
@@ -978,113 +916,14 @@ def lines(sep=$/)
end


def gsub(pattern, replacement=undefined)
# Because of the behavior of $~, this is duplicated from gsub! because
# if we call gsub! from gsub, the last_match can't be updated properly.

unless valid_encoding?
raise ArgumentError, "invalid byte sequence in #{encoding}"
end

if undefined.equal? replacement
unless block_given?
return to_enum(:gsub, pattern, replacement)
end
use_yield = true
tainted = false
else
tainted = replacement.tainted?
untrusted = replacement.untrusted?

unless replacement.kind_of?(String)
hash = Rubinius::Type.check_convert_type(replacement, Hash, :to_hash)
replacement = StringValue(replacement) unless hash
tainted ||= replacement.tainted?
untrusted ||= replacement.untrusted?
end
use_yield = false
end

pattern = Rubinius::Type.coerce_to_regexp(pattern, true) unless pattern.kind_of? Regexp
match = pattern.search_region(self, 0, bytesize, true)

unless match
Regexp.last_match = nil
end

duped = dup

last_end = 0
offset = nil

last_match = nil

ret = byteslice(0, 0) # Empty string and string subclass
offset = match.byte_begin(0) if match

while match
if str = match.pre_match_from(last_end)
ret.append str
end

if use_yield || hash
Regexp.last_match = match

if use_yield
val = yield match.to_s
else
val = hash[match.to_s]
end
untrusted = true if val.untrusted?
val = val.to_s unless val.kind_of?(String)

tainted ||= val.tainted?

ret.append val

if duped != self
raise RuntimeError, "string modified"
end
else
replacement.to_sub_replacement(ret, match)
end

tainted ||= val.tainted?

last_end = match.byte_end(0)

if match.collapsing?
if char = find_character(offset)
offset += char.bytesize
else
offset += 1
end
else
offset = match.byte_end(0)
end

last_match = match

match = pattern.match_from self, offset
break unless match

offset = match.byte_begin(0)
end

Regexp.last_match = last_match

str = byteslice(last_end, bytesize-last_end+1)
if str
ret.append str
end

ret.taint if tainted
ret.untrust if untrusted

ret
def gsub(pattern, replacement=undefined, &block)
s = dup
ret = s.gsub!(pattern, replacement, &block)
Truffle.invoke_primitive(:regexp_set_last_match, $~)
ret || s
end

def gsub!(pattern, replacement=undefined)
def gsub!(pattern, replacement=undefined, &block)
# Because of the behavior of $~, this is duplicated from gsub! because
# if we call gsub! from gsub, the last_match can't be updated properly.

@@ -1116,8 +955,10 @@ def gsub!(pattern, replacement=undefined)
pattern = Rubinius::Type.coerce_to_regexp(pattern, true) unless pattern.kind_of? Regexp
match = pattern.search_region(self, 0, bytesize, true)

Regexp.set_block_last_match(block, match) if block_given?

unless match
Regexp.last_match = nil
Truffle.invoke_primitive(:regexp_set_last_match, nil)
return nil
end

@@ -1137,7 +978,7 @@ def gsub!(pattern, replacement=undefined)
end

if use_yield || hash
Regexp.last_match = match
Truffle.invoke_primitive(:regexp_set_last_match, match)

if use_yield
val = yield match.to_s
@@ -1175,12 +1016,13 @@ def gsub!(pattern, replacement=undefined)
last_match = match

match = pattern.match_from self, offset
Regexp.set_block_last_match(block, match) if block_given?
break unless match

offset = match.byte_begin(0)
end

Regexp.last_match = last_match
Truffle.invoke_primitive(:regexp_set_last_match, last_match)

str = byteslice(last_end, bytesize-last_end+1)
if str
@@ -1204,7 +1046,7 @@ def match(pattern, pos=0)
else
pattern.match self, pos
end
Regexp.propagate_last_match
Truffle.invoke_primitive(:regexp_set_last_match, $~)
result
end

@@ -1551,10 +1393,10 @@ def index(str, start=undefined)
m = Rubinius::Mirror.reflect self
start = m.character_to_byte_index start
if match = str.match_from(self, start)
Regexp.last_match = match
Truffle.invoke_primitive(:regexp_set_last_match, match)
return match.begin(0)
else
Regexp.last_match = nil
Truffle.invoke_primitive(:regexp_set_last_match, nil)
return
end
end
@@ -1604,7 +1446,7 @@ def rindex(sub, finish=undefined)
Rubinius::Type.compatible_encoding self, sub

match_data = sub.search_region(self, 0, byte_finish, false)
Regexp.last_match = match_data
Truffle.invoke_primitive(:regexp_set_last_match, match_data)
return match_data.begin(0) if match_data

else
6 changes: 3 additions & 3 deletions truffle/src/main/ruby/core/symbol.rb
Original file line number Diff line number Diff line change
@@ -83,7 +83,7 @@ def match(pattern)
case pattern
when Regexp
match_data = pattern.search_region(str, 0, str.bytesize, true)
Regexp.last_match = match_data
Truffle.invoke_primitive(:regexp_set_last_match, match_data)
return match_data.byte_begin(0) if match_data
when String
raise TypeError, "type mismatch: String given"
@@ -112,13 +112,13 @@ def [](index, other = undefined)
if index.kind_of?(Regexp)
unless undefined.equal?(other)
match, str = to_s.send(:subpattern, index, other)
Regexp.last_match = match
Truffle.invoke_primitive(:regexp_set_last_match, match)
return str
end

str = to_s
match_data = index.search_region(str, 0, str.bytesize, true)
Regexp.last_match = match_data
Truffle.invoke_primitive(:regexp_set_last_match, match_data)
if match_data
result = match_data.to_s
Rubinius::Type.infect result, index

0 comments on commit f1b2a0e

Please sign in to comment.