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: 3b94d2257907
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 18b0d010a135
Choose a head ref
  • 4 commits
  • 8 files changed
  • 1 contributor

Commits on Sep 20, 2016

  1. Copy the full SHA
    75e6194 View commit details
  2. Copy the full SHA
    be52df4 View commit details
  3. Copy the full SHA
    beb3dfe View commit details
  4. Copy the full SHA
    18b0d01 View commit details
Original file line number Diff line number Diff line change
@@ -80,7 +80,6 @@
import org.jruby.truffle.core.rubinius.IOPrimitiveNodesFactory;
import org.jruby.truffle.core.rubinius.NativeFunctionPrimitiveNodesFactory;
import org.jruby.truffle.core.rubinius.RandomizerPrimitiveNodesFactory;
import org.jruby.truffle.core.rubinius.RegexpPrimitiveNodesFactory;
import org.jruby.truffle.core.rubinius.RubiniusTypeNodesFactory;
import org.jruby.truffle.core.rubinius.StatPrimitiveNodesFactory;
import org.jruby.truffle.core.rubinius.UndefinedPrimitiveNodesFactory;
@@ -738,7 +737,6 @@ public void addCoreMethods(PrimitiveManager primitiveManager) {
RandomizerPrimitiveNodesFactory.getFactories(),
RangeNodesFactory.getFactories(),
RegexpNodesFactory.getFactories(),
RegexpPrimitiveNodesFactory.getFactories(),
RubiniusTypeNodesFactory.getFactories(),
SizedQueueNodesFactory.getFactories(),
StatPrimitiveNodesFactory.getFactories(),
@@ -968,7 +966,7 @@ public void loadRubyCore() {
Main.printTruffleTimeMetric("before-load-core");
state = State.LOADING_RUBY_CORE;

@SuppressWarnings({ "rawtypes", "unchecked" })
@SuppressWarnings("unchecked")
final Future<RubyRootNode>[] coreFileFutures = new Future[coreFiles.length];

try {
Original file line number Diff line number Diff line change
@@ -85,8 +85,6 @@
import org.jruby.truffle.core.rope.RopeNodes;
import org.jruby.truffle.core.rope.RopeNodesFactory;
import org.jruby.truffle.core.rope.RopeOperations;
import org.jruby.truffle.core.rubinius.RegexpPrimitiveNodes;
import org.jruby.truffle.core.rubinius.RegexpPrimitiveNodesFactory;
import org.jruby.truffle.core.string.StringCachingGuards;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.core.symbol.SymbolTable;
195 changes: 192 additions & 3 deletions truffle/src/main/java/org/jruby/truffle/core/regexp/RegexpNodes.java
Original file line number Diff line number Diff line change
@@ -22,11 +22,16 @@
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.ImportStatic;
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.FrameDescriptor;
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.FrameSlotKind;
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;
@@ -50,15 +55,16 @@
import org.jruby.truffle.builtins.CoreMethod;
import org.jruby.truffle.builtins.CoreMethodArrayArgumentsNode;
import org.jruby.truffle.builtins.NonStandard;
import org.jruby.truffle.builtins.Primitive;
import org.jruby.truffle.builtins.PrimitiveArrayArgumentsNode;
import org.jruby.truffle.core.cast.ToStrNode;
import org.jruby.truffle.core.cast.ToStrNodeGen;
import org.jruby.truffle.core.regexp.RegexpNodesFactory.RegexpSetLastMatchPrimitiveNodeFactory;
import org.jruby.truffle.core.rope.CodeRange;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeNodes;
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;
@@ -384,6 +390,10 @@ public static DynamicObject createRubyRegexp(DynamicObjectFactory factory, Regex
return regexp;
}

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

@CoreMethod(names = "=~", required = 1)
public abstract static class MatchOperatorNode extends CoreMethodArrayArgumentsNode {

@@ -397,7 +407,7 @@ 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);
setLastMatchNode = RegexpSetLastMatchPrimitiveNodeFactory.create(null);
}

@Specialization(guards = "isRubyString(string)")
@@ -686,4 +696,183 @@ public DynamicObject allocate(DynamicObject rubyClass) {
}

}

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

@Specialization
public boolean fixedEncoding(DynamicObject regexp) {
return Layouts.REGEXP.getOptions(regexp).isFixed();
}

}

@Primitive(name = "regexp_initialize", lowerFixnum = 2)
@ImportStatic(RegexpGuards.class)
public static abstract class RegexpInitializePrimitiveNode extends PrimitiveArrayArgumentsNode {

@Specialization(guards = { "isRegexpLiteral(regexp)", "isRubyString(pattern)" })
public DynamicObject initializeRegexpLiteral(DynamicObject regexp, DynamicObject pattern, int options) {
throw new RaiseException(coreExceptions().securityError("can't modify literal regexp", this));
}

@Specialization(guards = { "!isRegexpLiteral(regexp)", "isInitialized(regexp)", "isRubyString(pattern)" })
public DynamicObject initializeAlreadyInitialized(DynamicObject regexp, DynamicObject pattern, int options) {
throw new RaiseException(coreExceptions().typeError("already initialized regexp", this));
}

@Specialization(guards = { "!isRegexpLiteral(regexp)", "!isInitialized(regexp)", "isRubyString(pattern)" })
public DynamicObject initialize(DynamicObject regexp, DynamicObject pattern, int options) {
RegexpNodes.initialize(getContext(), regexp, this, StringOperations.rope(pattern), options);
return regexp;
}

}

@Primitive(name = "regexp_options")
@ImportStatic(RegexpGuards.class)
public static abstract class RegexpOptionsPrimitiveNode extends PrimitiveArrayArgumentsNode {

@Specialization(guards = "isInitialized(regexp)")
public int options(DynamicObject regexp) {
return Layouts.REGEXP.getOptions(regexp).toOptions();
}

@Specialization(guards = "!isInitialized(regexp)")
public int optionsNotInitialized(DynamicObject regexp) {
throw new RaiseException(coreExceptions().typeError("uninitialized Regexp", this));
}

}

@Primitive(name = "regexp_search_region", lowerFixnum = { 2, 3 })
@ImportStatic(RegexpGuards.class)
public static abstract class RegexpSearchRegionPrimitiveNode extends PrimitiveArrayArgumentsNode {

@Specialization(guards = { "!isInitialized(regexp)", "isRubyString(string)" })
public Object searchRegionNotInitialized(DynamicObject regexp, DynamicObject string, int start, int end, boolean forward) {
throw new RaiseException(coreExceptions().typeError("uninitialized Regexp", this));
}

@Specialization(guards = { "isRubyString(string)", "!isValidEncoding(string)" })
public Object searchRegionInvalidEncoding(DynamicObject regexp, DynamicObject string, int start, int end, boolean forward) {
throw new RaiseException(coreExceptions().argumentError(formatError(string), this));
}

@TruffleBoundary
private String formatError(DynamicObject string) {
return StringUtils.format("invalid byte sequence in %s", Layouts.STRING.getRope(string).getEncoding());
}

@Specialization(guards = { "isInitialized(regexp)", "isRubyString(string)", "isValidEncoding(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, dupedString, false, matcher, start, end);
} else {
// Search backward through the string.
return RegexpNodes.matchCommon(getContext(), this, makeSubstringNode, regexp, dupedString, false, matcher, end, start);
}
}

}

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

public static RegexpSetLastMatchPrimitiveNode create() {
return RegexpSetLastMatchPrimitiveNodeFactory.create(null);
}

public abstract DynamicObject executeSetLastMatch(Object matchData);

@TruffleBoundary
@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", needsSelf = false)
@ImportStatic(RegexpNodes.class)
public static abstract class RegexpSetBlockLastMatchPrimitiveNode extends PrimitiveArrayArgumentsNode {

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

}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -114,6 +114,7 @@
import org.jruby.truffle.core.kernel.KernelNodesFactory;
import org.jruby.truffle.core.numeric.FixnumLowerNodeGen;
import org.jruby.truffle.core.numeric.FixnumOrBignumNode;
import org.jruby.truffle.core.regexp.RegexpNodes.RegexpSetLastMatchPrimitiveNode;
import org.jruby.truffle.core.rope.CodeRange;
import org.jruby.truffle.core.rope.ConcatRope;
import org.jruby.truffle.core.rope.LeafRope;
@@ -126,7 +127,6 @@
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;
@@ -606,7 +606,7 @@ public Object slice1(
DynamicObject regexp,
Object capture,
@Cached("createMethodCallIgnoreVisibility()") CallDispatchHeadNode callNode,
@Cached("create()") RegexpPrimitiveNodes.RegexpSetLastMatchPrimitiveNode setLastMatchNode) {
@Cached("create()") RegexpSetLastMatchPrimitiveNode setLastMatchNode) {
return sliceCapture(frame, string, regexp, 0, callNode, setLastMatchNode);
}

@@ -617,7 +617,7 @@ public Object sliceCapture(
DynamicObject regexp,
Object capture,
@Cached("createMethodCallIgnoreVisibility()") CallDispatchHeadNode callNode,
@Cached("create()") RegexpPrimitiveNodes.RegexpSetLastMatchPrimitiveNode setLastMatchNode) {
@Cached("create()") RegexpSetLastMatchPrimitiveNode setLastMatchNode) {
final Object matchStrPair = callNode.call(frame, string, "subpattern", regexp, capture);

if (matchStrPair == nil()) {
Original file line number Diff line number Diff line change
@@ -275,12 +275,8 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* A JRuby parser node visitor which translates JRuby AST nodes into truffle Nodes. Therefore there is some namespace
@@ -300,28 +296,6 @@ public class BodyTranslator extends Translator {

protected boolean usesRubiniusPrimitive = false;

private static final Set<String> THREAD_LOCAL_GLOBAL_VARIABLES = new HashSet<>(
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", "$!"));

private static final Map<String, String> GLOBAL_VARIABLE_ALIASES = new HashMap<String, String>();

static {
Map<String, String> m = GLOBAL_VARIABLE_ALIASES;
m.put("$-I", "$LOAD_PATH");
m.put("$:", "$LOAD_PATH");
m.put("$-d", "$DEBUG");
m.put("$-v", "$VERBOSE");
m.put("$-w", "$VERBOSE");
m.put("$-0", "$/");
m.put("$RS", "$/");
m.put("$INPUT_RECORD_SEPARATOR", "$/");
m.put("$>", "$stdout");
m.put("$PROGRAM_NAME", "$0");
}

public BodyTranslator(com.oracle.truffle.api.nodes.Node currentNode, RubyContext context, BodyTranslator parent, TranslatorEnvironment environment, Source source, boolean topLevel) {
super(currentNode, context, source);
parserSupport = new ParserSupport(context);
@@ -1793,9 +1767,7 @@ public RubyNode visitGlobalVarNode(GlobalVarParseNode node) {
} else {
readNode = new GetFromThreadLocalNode(context, fullSourceSection, readNode);
}
}

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

25 changes: 23 additions & 2 deletions truffle/src/main/java/org/jruby/truffle/parser/Translator.java
Original file line number Diff line number Diff line change
@@ -33,14 +33,35 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public abstract class Translator extends org.jruby.truffle.parser.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("$_", "$+", "$&", "$`", "$'", "$~", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9"));
public static final Set<String> FRAME_LOCAL_GLOBAL_VARIABLES = new HashSet<>(
Arrays.asList("$_", "$~", "$+", "$&", "$`", "$'", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9"));
static final Set<String> READ_ONLY_GLOBAL_VARIABLES = new HashSet<String>(
Arrays.asList("$:", "$LOAD_PATH", "$-I", "$\"", "$LOADED_FEATURES", "$<", "$FILENAME", "$?", "$-a", "$-l", "$-p", "$!"));
static final Set<String> ALWAYS_DEFINED_GLOBALS = new HashSet<>(Arrays.asList("$!", "$~"));
static final Set<String> THREAD_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$!", "$?")); // "$_"

static final Map<String, String> GLOBAL_VARIABLE_ALIASES = new HashMap<String, String>();
static {
Map<String, String> m = GLOBAL_VARIABLE_ALIASES;
m.put("$-I", "$LOAD_PATH");
m.put("$:", "$LOAD_PATH");
m.put("$-d", "$DEBUG");
m.put("$-v", "$VERBOSE");
m.put("$-w", "$VERBOSE");
m.put("$-0", "$/");
m.put("$RS", "$/");
m.put("$INPUT_RECORD_SEPARATOR", "$/");
m.put("$>", "$stdout");
m.put("$PROGRAM_NAME", "$0");
}

protected final Node currentNode;
protected final RubyContext context;
4 changes: 2 additions & 2 deletions truffle/src/main/ruby/core/string.rb
Original file line number Diff line number Diff line change
@@ -690,7 +690,7 @@ def slice!(one, two=undefined)
result = slice(one)

if one.kind_of? Regexp
lm = Regexp.last_match
lm = $~
self[one] = '' if result
Truffle.invoke_primitive(:regexp_set_last_match, lm)
else
@@ -700,7 +700,7 @@ def slice!(one, two=undefined)
result = slice(one, two)

if one.kind_of? Regexp
lm = Regexp.last_match
lm = $~
self[one, two] = '' if result
Truffle.invoke_primitive(:regexp_set_last_match, lm)
else