Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: ff976ff65f15^
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: d5e587fffc4d
Choose a head ref
  • 5 commits
  • 14 files changed
  • 1 contributor

Commits on Aug 13, 2016

  1. Copy the full SHA
    ff976ff View commit details
  2. [Truffle] Replaced calls to Regexp.last_match= with a direct primitiv…

    …e invoke to cut out an extra frame.
    nirvdrum committed Aug 13, 2016
    Copy the full SHA
    29f2fff View commit details
  3. [Truffle] Simplified the RegexpNodes#matchCommon call a bit by removi…

    …ng support for 'operator' mode.
    nirvdrum committed Aug 13, 2016
    Copy the full SHA
    ef82719 View commit details
  4. [Truffle] Made $~ and related global variables frame-local.

    Prior to this change we treated $~ as thread-local. While that worked in many cases, it did mean that the match data lived longer than it should. Fixing this is a bit tricky because generally the match data should not propagate up the call stack beyond the caller, but we implement much of our core library in Ruby itself. So we must propagate values back to the user's call, and then cease propagating up the call stack.
    nirvdrum committed Aug 13, 2016
    Copy the full SHA
    9120332 View commit details
  5. Copy the full SHA
    d5e587f View commit details
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@
import org.jruby.truffle.language.locals.WriteFrameSlotNodeGen;
import org.jruby.truffle.language.objects.AllocateObjectNode;
import org.jruby.truffle.language.objects.AllocateObjectNodeGen;
import org.jruby.truffle.language.parser.jruby.Translator;
import org.jruby.truffle.language.threadlocal.ThreadLocalObject;

import java.util.ArrayList;
@@ -310,7 +311,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
@@ -36,6 +36,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;
@@ -173,6 +174,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) {
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;
@@ -99,15 +105,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);
@@ -124,8 +130,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();) {
@@ -161,8 +165,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();) {
@@ -190,11 +192,7 @@ public Integer block() throws InterruptedException {
}
}

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

// because the factory is not constant
@@ -391,17 +389,29 @@ public static DynamicObject createRubyRegexp(DynamicObjectFactory factory, Regex
public abstract static class MatchOperatorNode extends CoreMethodArrayArgumentsNode {

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

@Specialization(guards = "isRubySymbol(symbol)")
@@ -455,7 +465,7 @@ public MatchStartNode(RubyContext context, SourceSection sourceSection) {

@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);
final Object matchResult = matchCommon(getContext(), this, makeSubstringNode, regexp, string, 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;
@@ -464,6 +474,60 @@ public Object matchStart(DynamicObject regexp, DynamicObject string, int startPo
}
}

@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(FrameInstance.FrameAccess.READ_WRITE, 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;
try {
previousMatchData = frame.getObject(slot);

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

return threadLocalObject.get();
} else {
return previousMatchData;
}
} catch (FrameSlotTypeException e) {
throw new IllegalStateException(e);
}
}

}

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

@@ -509,7 +573,7 @@ public SearchFromNode(RubyContext context, SourceSection sourceSection) {

@Specialization(guards = "isRubyString(string)")
public Object searchFrom(DynamicObject regexp, DynamicObject string, int startPos) {
return matchCommon(getContext(), this, makeSubstringNode, regexp, string, false, false, startPos);
return matchCommon(getContext(), this, makeSubstringNode, regexp, string, false, startPos);
}
}

Original file line number Diff line number Diff line change
@@ -13,17 +13,24 @@
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.object.DynamicObject;
import org.joni.Matcher;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.builtins.Primitive;
import org.jruby.truffle.builtins.PrimitiveArrayArgumentsNode;
import org.jruby.truffle.core.regexp.RegexpGuards;
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.arguments.RubyArguments;
import org.jruby.truffle.language.control.RaiseException;
import org.jruby.truffle.language.threadlocal.ThreadLocalObject;
import org.jruby.truffle.util.StringUtils;

/**
@@ -80,17 +87,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 {
@@ -118,40 +114,104 @@ public Object searchRegion(DynamicObject regexp, DynamicObject string, int start

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, string, 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, string, false, matcher, end, start);
}
}

}

@Primitive(name = "regexp_set_last_match")
@Primitive(name = "regexp_set_last_match", needsSelf = false)
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
public DynamicObject setLastMatchData(DynamicObject matchData) {
// TODO (nirvdrum 08-Aug-16): Validate that the matchData is either nil or a MatchData object, otherwise throw an exception. It should use the same logic as assigning $~ does in the translator.

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)
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)")
public Object setBlockLastMatch(DynamicObject block, DynamicObject matchData) {

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

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

Frame tempFrame = RubyArguments.getDeclarationFrame(callerFrame);

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

final FrameDescriptor callerFrameDescriptor = callerFrame.getFrameDescriptor();

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

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

final Object matchDataHolder = callerFrame.getObject(frameSlot);

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

return matchData;
}

}
Loading