Skip to content

Commit

Permalink
Showing 40 changed files with 665 additions and 167 deletions.
3 changes: 2 additions & 1 deletion core/src/main/java/org/jruby/truffle/TruffleBridgeImpl.java
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.TruffleBridge;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.truffle.nodes.RubyNode;
@@ -161,7 +162,7 @@ public Object execute(final TranslatorDriver.ParserContext parserContext, final
source = Source.fromBytes(bytes, inputFile, new BytesDecoder.UTF8BytesDecoder());
}

return truffleContext.execute(truffleContext, source, parserContext, self, parentFrame, null, new NodeWrapper() {
return truffleContext.execute(truffleContext, source, UTF8Encoding.INSTANCE, parserContext, self, parentFrame, null, new NodeWrapper() {
@Override
public RubyNode wrap(RubyNode node) {
RubyContext context = node.getContext();
29 changes: 27 additions & 2 deletions core/src/main/java/org/jruby/truffle/nodes/cast/ArrayCastNode.java
Original file line number Diff line number Diff line change
@@ -23,19 +23,30 @@
import org.jruby.truffle.runtime.core.RubyBignum;
import org.jruby.truffle.runtime.core.RubyNilClass;

/*
* TODO(CS): could probably unify this with SplatCastNode with some final configuration options.
*/
@NodeChild("child")
public abstract class ArrayCastNode extends RubyNode {

private final SplatCastNode.NilBehavior nilBehavior;

@Child protected DispatchHeadNode toArrayNode;

public ArrayCastNode(RubyContext context, SourceSection sourceSection) {
this(context, sourceSection, SplatCastNode.NilBehavior.NIL);
}

public ArrayCastNode(RubyContext context, SourceSection sourceSection, SplatCastNode.NilBehavior nilBehavior) {
super(context, sourceSection);
toArrayNode = new DispatchHeadNode(context, Dispatch.MissingBehavior.RETURN_MISSING);
this.nilBehavior = nilBehavior;
}

public ArrayCastNode(ArrayCastNode prev) {
super(prev);
toArrayNode = prev.toArrayNode;
nilBehavior = prev.nilBehavior;
}

protected abstract RubyNode getChild();
@@ -71,8 +82,22 @@ public RubyArray cast(RubyArray array) {
}

@Specialization
public RubyNilClass cast(RubyNilClass nil) {
return nil;
public Object cast(RubyNilClass nil) {
switch (nilBehavior) {
case EMPTY_ARRAY:
return new RubyArray(getContext().getCoreLibrary().getArrayClass());

case ARRAY_WITH_NIL:
return RubyArray.fromObject(getContext().getCoreLibrary().getArrayClass(), getContext().getCoreLibrary().getNilObject());

case NIL:
return nil;

default: {
CompilerAsserts.neverPartOfCompilation();
throw new UnsupportedOperationException();
}
}
}

@Specialization(guards = {"!isRubyNilClass", "!isRubyArray"})
101 changes: 101 additions & 0 deletions core/src/main/java/org/jruby/truffle/nodes/cast/HashCastNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.cast;

import com.oracle.truffle.api.CompilerDirectives;
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.nodes.RubyNode;
import org.jruby.truffle.nodes.dispatch.Dispatch;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.*;

// TODO(CS): copy and paste of ArrayCastNode

@NodeChild("child")
public abstract class HashCastNode extends RubyNode {

@Child protected DispatchHeadNode toHashNode;

public HashCastNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
toHashNode = new DispatchHeadNode(context, Dispatch.MissingBehavior.RETURN_MISSING);
}

public HashCastNode(HashCastNode prev) {
super(prev);
toHashNode = prev.toHashNode;
}

protected abstract RubyNode getChild();

@Specialization
public RubyNilClass cast(boolean value) {
return getContext().getCoreLibrary().getNilObject();
}

@Specialization
public RubyNilClass cast(int value) {
return getContext().getCoreLibrary().getNilObject();
}

@Specialization
public RubyNilClass cast(long value) {
return getContext().getCoreLibrary().getNilObject();
}

@Specialization
public RubyNilClass cast(double value) {
return getContext().getCoreLibrary().getNilObject();
}

@Specialization
public RubyNilClass cast(RubyBignum value) {
return getContext().getCoreLibrary().getNilObject();
}

@Specialization
public RubyHash cast(RubyHash hash) {
return hash;
}

@Specialization
public RubyNilClass cast(RubyNilClass nil) {
return nil;
}

@Specialization(guards = {"!isRubyNilClass", "!isRubyHash"})
public Object cast(VirtualFrame frame, RubyBasicObject object) {
notDesignedForCompilation();

final Object result = toHashNode.call(frame, object, "to_hash", null, new Object[]{});

if (result == Dispatch.MISSING) {
return getContext().getCoreLibrary().getNilObject();
}

if (!(result instanceof RubyHash)) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().typeErrorShouldReturn(object.toString(), "to_hash", "HAsh", this));
}

return result;
}

@Override
public void executeVoid(VirtualFrame frame) {
getChild().executeVoid(frame);
}

}
Original file line number Diff line number Diff line change
@@ -33,7 +33,8 @@ public abstract class SplatCastNode extends RubyNode {

public static enum NilBehavior {
EMPTY_ARRAY,
ARRAY_WITH_NIL
ARRAY_WITH_NIL,
NIL
}

private final NilBehavior nilBehavior;
@@ -91,9 +92,8 @@ public RubyArray splat(VirtualFrame frame, RubyArray array) {
public RubyArray splat(VirtualFrame frame, Object object) {
notDesignedForCompilation();

RubyString toAString = getContext().makeString("to_a"); // TODO

if (respondToCast.executeBoolean(frame, respondToToA.call(frame, object, "respond_to?", null, toAString, true))) {
// TODO(CS): why are we directly calling #respond_to? instead of using a respondsTo on a dispatch head node?
if (respondToCast.executeBoolean(frame, respondToToA.call(frame, object, "respond_to?", null, getContext().makeString("to_a"), true))) {
final Object array = toA.call(frame, object, "to_a", null);

if (array instanceof RubyArray) {
Original file line number Diff line number Diff line change
@@ -18,7 +18,6 @@
import org.jruby.truffle.nodes.methods.ExceptionTranslatingNode;
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.runtime.control.*;
import org.jruby.truffle.runtime.core.RubyBasicObject;

/**
* Represents a block of code run with exception handlers. There's no {@code try} keyword in Ruby -
@@ -73,7 +72,8 @@ public Object execute(VirtualFrame frame) {
private Object handleException(VirtualFrame frame, RaiseException exception) {
CompilerAsserts.neverPartOfCompilation();

getContext().getCoreLibrary().getGlobalVariablesObject().setInstanceVariable("$!", exception.getRubyException());
notDesignedForCompilation();
getContext().getCoreLibrary().getGlobalVariablesObject().getOperations().setInstanceVariable(getContext().getCoreLibrary().getGlobalVariablesObject(), "$!", exception.getRubyException());

for (RescueNode rescue : rescueParts) {
if (rescue.canHandle(frame, exception.getRubyException())) {
Original file line number Diff line number Diff line change
@@ -1948,6 +1948,13 @@ public RubyArray initialize(VirtualFrame frame, RubyArray array, int size, Undef
return array;
}

@Specialization
public RubyArray initialize(RubyArray array, RubyArray copy, UndefinedPlaceholder defaultValue, UndefinedPlaceholder block) {
notDesignedForCompilation();
array.setStore(copy.slowToArray(), copy.getSize());
return array;
}

}

@CoreMethod(names = "initialize_copy", visibility = Visibility.PRIVATE, required = 1)
Original file line number Diff line number Diff line change
@@ -234,7 +234,7 @@ public InstanceEvalNode(InstanceEvalNode prev) {
public Object instanceEval(VirtualFrame frame, Object receiver, RubyString string, UndefinedPlaceholder block) {
notDesignedForCompilation();

return getContext().eval(string.toString(), receiver, this);
return getContext().eval(string.getBytes(), receiver, this);
}

@Specialization
Original file line number Diff line number Diff line change
@@ -14,8 +14,10 @@
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyEncoding;
import org.jruby.truffle.runtime.core.RubyRegexp;
import org.jruby.truffle.runtime.core.RubyString;
import org.jruby.truffle.translator.BodyTranslator;
import org.jruby.util.RegexpOptions;

public class InteroplatedRegexpNode extends RubyNode {
@@ -43,7 +45,20 @@ public RubyRegexp executeRubyRegexp(VirtualFrame frame) {
}

final org.jruby.RubyString preprocessed = org.jruby.RubyRegexp.preprocessDRegexp(getContext().getRuntime(), strings, options);
return new RubyRegexp(this, getContext().getCoreLibrary().getRegexpClass(), preprocessed.getByteList(), options.toOptions());

final RubyRegexp regexp = new RubyRegexp(this, getContext().getCoreLibrary().getRegexpClass(), preprocessed.getByteList(), options.toOptions());

if (options.isEncodingNone()) {
// This isn't quite right - we shouldn't be looking up by name, we need a real reference to this constants

if (!BodyTranslator.all7Bit(preprocessed.getByteList().bytes())) {
regexp.forceEncoding((RubyEncoding) getContext().getCoreLibrary().getEncodingClass().getConstants().get("ASCII_8BIT").getValue());
} else {
regexp.forceEncoding((RubyEncoding) getContext().getCoreLibrary().getEncodingClass().getConstants().get("US_ASCII").getValue());
}
}

return regexp;
}

@Override
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
import com.oracle.truffle.api.source.*;
import com.oracle.truffle.api.frame.*;
import com.oracle.truffle.api.nodes.*;
import com.oracle.truffle.api.utilities.ConditionProfile;
import org.jruby.truffle.nodes.*;
import org.jruby.truffle.nodes.cast.ToSNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
@@ -28,21 +29,38 @@ public final class InterpolatedStringNode extends RubyNode {

@Children protected final ToSNode[] children;

@Child protected KernelNodes.TaintedNode taintedNode;
@Child protected KernelNodes.TaintNode taintNode;

private final ConditionProfile taintProfile = ConditionProfile.createCountingProfile();

public InterpolatedStringNode(RubyContext context, SourceSection sourceSection, ToSNode[] children) {
super(context, sourceSection);
this.children = children;
taintedNode = KernelNodesFactory.TaintedNodeFactory.create(context, sourceSection, new RubyNode[]{});
taintNode = KernelNodesFactory.TaintNodeFactory.create(context, sourceSection, new RubyNode[]{});
}

@ExplodeLoop
@Override
public Object execute(VirtualFrame frame) {
final RubyString[] strings = new RubyString[children.length];

boolean tainted = false;

for (int n = 0; n < children.length; n++) {
strings[n] = children[n].executeString(frame);
final RubyString toInterpolate = children[n].executeString(frame);
strings[n] = toInterpolate;
tainted |= taintedNode.tainted(toInterpolate);
}

final RubyString string = concat(strings);

if (taintProfile.profile(tainted)) {
taintNode.taint(string);
}

return concat(strings);
return string;
}

@CompilerDirectives.TruffleBoundary
172 changes: 162 additions & 10 deletions core/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.*;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.source.*;
import com.oracle.truffle.api.dsl.*;
import com.oracle.truffle.api.frame.*;
@@ -28,6 +29,8 @@
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.PredicateDispatchHeadNode;
import org.jruby.truffle.nodes.literal.*;
import org.jruby.truffle.nodes.objectstorage.ReadHeadObjectFieldNode;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNode;
import org.jruby.truffle.nodes.yield.*;
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.runtime.backtrace.Activation;
@@ -341,7 +344,8 @@ public Object doCatch(VirtualFrame frame, Object tag, RubyProc block) {
} catch (ThrowException e) {
if (e.getTag().equals(tag)) {
// TODO(cs): unset rather than set to Nil?
getContext().getCoreLibrary().getGlobalVariablesObject().setInstanceVariable("$!", getContext().getCoreLibrary().getNilObject());
notDesignedForCompilation();
getContext().getCoreLibrary().getGlobalVariablesObject().getOperations().setInstanceVariable(getContext().getCoreLibrary().getGlobalVariablesObject(), "$!", getContext().getCoreLibrary().getNilObject());
return e.getValue();
} else {
throw e;
@@ -430,7 +434,7 @@ public Object clone(VirtualFrame frame, RubyBasicObject self) {
newObject.getSingletonClass(this).initCopy(self.getMetaClass());
}

newObject.setInstanceVariables(self.getInstanceVariables());
newObject.getOperations().setInstanceVariables(newObject, self.getOperations().getInstanceVariables(self));
initializeCloneNode.call(frame, newObject, "initialize_clone", null, self);

return newObject;
@@ -459,7 +463,7 @@ public Object dup(VirtualFrame frame, RubyBasicObject self) {
// This method is pretty crappy for compilation - it should improve with the OM

final RubyBasicObject newObject = self.getLogicalClass().newInstance(this);
newObject.setInstanceVariables(self.getInstanceVariables());
newObject.getOperations().setInstanceVariables(newObject, self.getOperations().getInstanceVariables(self));
initializeDupNode.call(frame, newObject, "initialize_dup", null, self);

return newObject;
@@ -513,7 +517,7 @@ public Object eval(VirtualFrame frame, RubyString source, UndefinedPlaceholder b
public Object eval(RubyString source, RubyBinding binding) {
notDesignedForCompilation();

return getContext().eval(source.toString(), binding, this);
return getContext().eval(source.getBytes(), binding, this);
}

@Specialization(guards = "!isRubyString(arguments[0])")
@@ -543,7 +547,7 @@ public Object eval(VirtualFrame frame, RubyBasicObject object, RubyBinding bindi
}

if (coerced instanceof RubyString) {
return getContext().eval(coerced.toString(), binding, this);
return getContext().eval(((RubyString) coerced).getBytes(), binding, this);
} else {
throw new RaiseException(
getContext().getCoreLibrary().typeError(
@@ -999,15 +1003,17 @@ public InstanceVariableSetNode(InstanceVariableSetNode prev) {
public Object isInstanceVariableSet(RubyBasicObject object, RubyString name, Object value) {
notDesignedForCompilation();

object.setInstanceVariable(RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
notDesignedForCompilation();
object.getOperations().setInstanceVariable(object, RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
return value;
}

@Specialization
public Object isInstanceVariableSet(RubyBasicObject object, RubySymbol name, Object value) {
notDesignedForCompilation();

object.setInstanceVariable(RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
notDesignedForCompilation();
object.getOperations().setInstanceVariable(object, RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
return value;
}

@@ -1028,14 +1034,16 @@ public InstanceVariablesNode(InstanceVariablesNode prev) {
public RubyArray instanceVariables(RubyBasicObject self) {
notDesignedForCompilation();

final String[] instanceVariableNames = self.getFieldNames();
final Object[] instanceVariableNames = self.getOperations().getFieldNames(self);

Arrays.sort(instanceVariableNames);

final RubyArray array = new RubyArray(getContext().getCoreLibrary().getArrayClass());

for (String name : instanceVariableNames) {
array.slowPush(getContext().getSymbolTable().getSymbol(name));
for (Object name : instanceVariableNames) {
if (name instanceof String) {
array.slowPush(getContext().getSymbolTable().getSymbol((String) name));
}
}

return array;
@@ -1987,6 +1995,101 @@ public Object fork(Object[] args) {

}

@CoreMethod(names = "taint")
public abstract static class TaintNode extends CoreMethodNode {

@Child protected WriteHeadObjectFieldNode writeTaintNode;

public TaintNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
writeTaintNode = new WriteHeadObjectFieldNode(RubyBasicObject.TAINTED_IDENTIFIER);
}

public TaintNode(TaintNode prev) {
super(prev);
writeTaintNode = prev.writeTaintNode;
}

@Specialization
public Object taint(boolean object) {
return frozen(object);
}

@Specialization
public Object taint(int object) {
return frozen(object);
}

@Specialization
public Object taint(long object) {
return frozen(object);
}

@Specialization
public Object taint(double object) {
return frozen(object);
}

private Object frozen(Object object) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().frozenError(getContext().getCoreLibrary().getLogicalClass(object).getName(), this));
}


@Specialization
public Object taint(RubyBasicObject object) {
writeTaintNode.execute(object, true);
return object;
}

}

@CoreMethod(names = "tainted?")
public abstract static class TaintedNode extends CoreMethodNode {

@Child protected ReadHeadObjectFieldNode readTaintNode;

public TaintedNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
readTaintNode = new ReadHeadObjectFieldNode(RubyBasicObject.TAINTED_IDENTIFIER);
}

public TaintedNode(TaintedNode prev) {
super(prev);
readTaintNode = prev.readTaintNode;
}

@Specialization
public boolean tainted(boolean object) {
return false;
}

@Specialization
public boolean tainted(int object) {
return false;
}

@Specialization
public boolean tainted(long object) {
return false;
}

@Specialization
public boolean tainted(double object) {
return false;
}

@Specialization
public boolean tainted(RubyBasicObject object) {
try {
return readTaintNode.isSet(object) && readTaintNode.executeBoolean(object);
} catch (UnexpectedResultException e) {
throw new UnsupportedOperationException();
}
}

}

@CoreMethod(names = "throw", isModuleFunction = true, required = 1, optional = 1)
public abstract static class ThrowNode extends CoreMethodNode {

@@ -2086,4 +2189,53 @@ public RubyString toS(VirtualFrame frame, Object self) {

}

@CoreMethod(names = "untaint")
public abstract static class UntaintNode extends CoreMethodNode {

@Child protected WriteHeadObjectFieldNode writeTaintNode;

public UntaintNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
writeTaintNode = new WriteHeadObjectFieldNode(RubyBasicObject.TAINTED_IDENTIFIER);
}

public UntaintNode(UntaintNode prev) {
super(prev);
writeTaintNode = prev.writeTaintNode;
}

@Specialization
public Object taint(boolean object) {
return frozen(object);
}

@Specialization
public Object taint(int object) {
return frozen(object);
}

@Specialization
public Object taint(long object) {
return frozen(object);
}

@Specialization
public Object taint(double object) {
return frozen(object);
}

private Object frozen(Object object) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().frozenError(getContext().getCoreLibrary().getLogicalClass(object).getName(), this));
}


@Specialization
public Object taint(RubyBasicObject object) {
writeTaintNode.execute(object, false);
return object;
}

}

}
12 changes: 6 additions & 6 deletions core/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java
Original file line number Diff line number Diff line change
@@ -391,24 +391,24 @@ public ClassEvalNode(ClassEvalNode prev) {
public Object classEval(VirtualFrame frame, RubyModule module, RubyString code, @SuppressWarnings("unused") UndefinedPlaceholder file, @SuppressWarnings("unused") UndefinedPlaceholder line, @SuppressWarnings("unused") UndefinedPlaceholder block) {
notDesignedForCompilation();

final Source source = Source.fromText(code.toString(), "(eval)");
return getContext().execute(getContext(), source, TranslatorDriver.ParserContext.MODULE, module, frame.materialize(), this);
final Source source = Source.fromText(code.getBytes(), "(eval)");
return getContext().execute(getContext(), source, code.getBytes().getEncoding(), TranslatorDriver.ParserContext.MODULE, module, frame.materialize(), this);
}

@Specialization
public Object classEval(VirtualFrame frame, RubyModule module, RubyString code, RubyString file, @SuppressWarnings("unused") UndefinedPlaceholder line, @SuppressWarnings("unused") UndefinedPlaceholder block) {
notDesignedForCompilation();

final Source source = Source.asPseudoFile(code.toString(), file.toString());
return getContext().execute(getContext(), source, TranslatorDriver.ParserContext.MODULE, module, frame.materialize(), this);
final Source source = Source.asPseudoFile(code.getBytes(), file.toString());
return getContext().execute(getContext(), source, code.getBytes().getEncoding(), TranslatorDriver.ParserContext.MODULE, module, frame.materialize(), this);
}

@Specialization
public Object classEval(VirtualFrame frame, RubyModule module, RubyString code, RubyString file, @SuppressWarnings("unused") int line, @SuppressWarnings("unused") UndefinedPlaceholder block) {
notDesignedForCompilation();

final Source source = Source.asPseudoFile(code.toString(), file.toString());
return getContext().execute(getContext(), source, TranslatorDriver.ParserContext.MODULE, module, frame.materialize(), this);
final Source source = Source.asPseudoFile(code.getBytes(), file.toString());
return getContext().execute(getContext(), source, code.getBytes().getEncoding(), TranslatorDriver.ParserContext.MODULE, module, frame.materialize(), this);
}

@Specialization
20 changes: 20 additions & 0 deletions core/src/main/java/org/jruby/truffle/nodes/core/RegexpNodes.java
Original file line number Diff line number Diff line change
@@ -43,6 +43,26 @@ protected RubyString escape(VirtualFrame frame, RubyString string) {
}
}

public abstract static class EscapingYieldingNode extends YieldingCoreMethodNode {
@Child protected EscapeNode escapeNode;

public EscapingYieldingNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public EscapingYieldingNode(EscapingYieldingNode prev) {
super(prev);
}

protected RubyString escape(VirtualFrame frame, RubyString string) {
if (escapeNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
escapeNode = insert(RegexpNodesFactory.EscapeNodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{null}));
}
return escapeNode.executeEscape(frame, string);
}
}

@CoreMethod(names = "==", required = 1)
public abstract static class EqualNode extends CoreMethodNode {

95 changes: 89 additions & 6 deletions core/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java
Original file line number Diff line number Diff line change
@@ -15,8 +15,11 @@
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.utilities.BranchProfile;
import org.jcodings.Encoding;
import org.jcodings.specific.ASCIIEncoding;
import org.joni.Matcher;
import org.joni.Option;
import org.joni.Region;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.runtime.Visibility;
@@ -27,8 +30,11 @@
import org.jruby.truffle.runtime.core.RubyRange;
import org.jruby.util.ByteList;
import org.jruby.util.Pack;
import org.jruby.util.StringSupport;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -588,30 +594,107 @@ public RubyString forceEncoding(RubyString string, RubyEncoding encoding) {

}

@CoreMethod(names = "gsub", required = 2)
public abstract static class GsubNode extends RegexpNodes.EscapingNode {
@CoreMethod(names = "gsub", required = 1, optional = 1, needsBlock = true)
public abstract static class GsubNode extends RegexpNodes.EscapingYieldingNode {

@Child protected DispatchHeadNode toS;

public GsubNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
toS = new DispatchHeadNode(context);
}

public GsubNode(GsubNode prev) {
super(prev);
toS = prev.toS;
}

@Specialization
public RubyString gsub(VirtualFrame frame, RubyString string, RubyString regexpString, RubyString replacement) {
public RubyString gsub(VirtualFrame frame, RubyString string, RubyString regexpString, RubyString replacement, UndefinedPlaceholder block) {
notDesignedForCompilation();

final RubyRegexp regexp = new RubyRegexp(this, getContext().getCoreLibrary().getRegexpClass(), escape(frame, regexpString).getBytes(), Option.DEFAULT);
return gsub(string, regexp, replacement);
return gsub(string, regexp, replacement, block);
}

@Specialization
public RubyString gsub(RubyString string, RubyRegexp regexp, RubyString replacement, @SuppressWarnings("unused") UndefinedPlaceholder block) {
notDesignedForCompilation();

return regexp.gsub(string, replacement.toString());
}

@Specialization
public RubyString gsub(RubyString string, RubyRegexp regexp, RubyString replacement) {
public RubyString gsub(VirtualFrame frame, RubyString string, RubyString regexpString, RubyString replacement, RubyProc block) {
notDesignedForCompilation();

return regexp.gsub(string.toString(), replacement.toString());
final RubyRegexp regexp = new RubyRegexp(this, getContext().getCoreLibrary().getRegexpClass(), escape(frame, regexpString).getBytes(), Option.DEFAULT);
return gsub(string, regexp, replacement, block);
}

@Specialization
public RubyString gsub(RubyString string, RubyRegexp regexp, RubyString replacement, @SuppressWarnings("unused") RubyProc block) {
notDesignedForCompilation();

return regexp.gsub(string, replacement.toString());
}

@Specialization
public RubyString gsub(VirtualFrame frame, RubyString string, RubyString regexpString, @SuppressWarnings("unused") UndefinedPlaceholder replacement, RubyProc block) {
notDesignedForCompilation();

final RubyRegexp regexp = new RubyRegexp(this, getContext().getCoreLibrary().getRegexpClass(), escape(frame, regexpString).getBytes(), Option.DEFAULT);
return gsub(frame, string, regexp, replacement, block);
}

@Specialization
public RubyString gsub(VirtualFrame frame, RubyString string, RubyRegexp regexp, @SuppressWarnings("unused") UndefinedPlaceholder replacement, RubyProc block) {
notDesignedForCompilation();

final RubyContext context = getContext();

final byte[] stringBytes = string.getBytes().bytes();
final Encoding encoding = string.getBytes().getEncoding();
final Matcher matcher = regexp.getRegex().matcher(stringBytes);

int p = string.getBytes().getBegin();
int end = 0;
int range = p + string.getBytes().getRealSize();
int lastMatchEnd = 0;

// We only ever care about the entire matched string, not each of the matched parts, so we can hard-code the index.
int matchedStringIndex = 0;

final StringBuilder builder = new StringBuilder();

while (true) {
Object matchData = regexp.matchCommon(string.getBytes(), false, true, matcher, p + end, range);

if (matchData == context.getCoreLibrary().getNilObject()) {
builder.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(stringBytes, lastMatchEnd, range - lastMatchEnd)));

break;
}

Region region = matcher.getEagerRegion();

RubyMatchData md = (RubyMatchData) matchData;
Object[] values = md.getValues();

int regionStart = region.beg[matchedStringIndex];
int regionEnd = region.end[matchedStringIndex];

// TODO (nirvdrum Dec. 24, 2014): There's probably a better way of doing this than converting back and forth between String and RubyString.
builder.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(stringBytes, lastMatchEnd, regionStart - lastMatchEnd)));

Object yieldResult = yield(frame, block, values[matchedStringIndex]);
builder.append(toS.call(frame, yieldResult, "to_s", null).toString());

lastMatchEnd = regionEnd;
end = StringSupport.positionEndForScan(string.getBytes(), matcher, encoding, p, range);
}

return context.makeString(builder.toString());
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.literal;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyHash;
import org.jruby.truffle.runtime.hash.HashOperations;
import org.jruby.truffle.runtime.hash.KeyValue;

import java.util.ArrayList;
import java.util.List;

public class ConcatHashLiteralNode extends RubyNode {

@Children protected final RubyNode[] children;

public ConcatHashLiteralNode(RubyContext context, SourceSection sourceSection, RubyNode[] children) {
super(context, sourceSection);
this.children = children;
}

@Override
public RubyHash executeRubyHash(VirtualFrame frame) {
notDesignedForCompilation();

final List<KeyValue> keyValues = new ArrayList<>();

for (RubyNode child : children) {
try {
keyValues.addAll(HashOperations.verySlowToKeyValues(child.executeRubyHash(frame)));
} catch (UnexpectedResultException e) {
throw new UnsupportedOperationException();
}
}

return HashOperations.verySlowFromEntries(getContext(), keyValues);
}

@Override
public Object execute(VirtualFrame frame) {
return executeRubyHash(frame);
}

}
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ public class ReadInstanceVariableNode extends RubyNode implements ReadNode {
private final BranchProfile nullProfile = BranchProfile.create();
private final BranchProfile primitiveProfile = BranchProfile.create();

public ReadInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, boolean isGlobal) {
public ReadInstanceVariableNode(RubyContext context, SourceSection sourceSection, Object name, RubyNode receiver, boolean isGlobal) {
super(context, sourceSection);
this.receiver = receiver;
readNode = new ReadHeadObjectFieldNode(name);
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ public class WriteInstanceVariableNode extends RubyNode implements WriteNode {
@Child protected WriteHeadObjectFieldNode writeNode;
private final boolean isGlobal;

public WriteInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, RubyNode rhs, boolean isGlobal) {
public WriteInstanceVariableNode(RubyContext context, SourceSection sourceSection, Object name, RubyNode receiver, RubyNode rhs, boolean isGlobal) {
super(context, sourceSection);
this.receiver = receiver;
this.rhs = rhs;
Original file line number Diff line number Diff line change
@@ -15,14 +15,18 @@

public class ReadHeadObjectFieldNode extends Node {

private final String name;
private final Object name;
@Child protected ReadObjectFieldNode first;

public ReadHeadObjectFieldNode(String name) {
public ReadHeadObjectFieldNode(Object name) {
this.name = name;
first = new UninitializedReadObjectFieldNode(name);
}

public boolean executeBoolean(RubyBasicObject object) throws UnexpectedResultException {
return first.executeBoolean(object);
}

public int executeInteger(RubyBasicObject object) throws UnexpectedResultException {
return first.executeInteger(object);
}
@@ -39,7 +43,7 @@ public Object execute(RubyBasicObject object) {
return first.execute(object);
}

public String getName() {
public Object getName() {
return name;
}

Original file line number Diff line number Diff line change
@@ -18,9 +18,9 @@
@NodeInfo(cost = NodeCost.UNINITIALIZED)
public class UninitializedReadObjectFieldNode extends ReadObjectFieldNode {

private final String name;
private final Object name;

public UninitializedReadObjectFieldNode(String name) {
public UninitializedReadObjectFieldNode(Object name) {
this.name = name;
}

Original file line number Diff line number Diff line change
@@ -18,8 +18,8 @@
@NodeInfo(cost = NodeCost.UNINITIALIZED)
public class UninitializedWriteObjectFieldNode extends WriteObjectFieldNode {

private final String name;
public UninitializedWriteObjectFieldNode(String name) {
private final Object name;
public UninitializedWriteObjectFieldNode(Object name) {
this.name = name;

}
Original file line number Diff line number Diff line change
@@ -14,10 +14,10 @@

public class WriteHeadObjectFieldNode extends Node {

private final String name;
private final Object name;
@Child protected WriteObjectFieldNode first;

public WriteHeadObjectFieldNode(String name) {
public WriteHeadObjectFieldNode(Object name) {
this.name = name;
first = new UninitializedWriteObjectFieldNode(name);
}
@@ -38,7 +38,7 @@ public void execute(RubyBasicObject object, Object value) {
first.execute(object, value);
}

public String getName() {
public Object getName() {
return name;
}

26 changes: 14 additions & 12 deletions core/src/main/java/org/jruby/truffle/runtime/RubyContext.java
Original file line number Diff line number Diff line change
@@ -18,6 +18,8 @@
import java.util.concurrent.atomic.*;

import com.oracle.truffle.api.object.Shape;
import org.jcodings.Encoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.Ruby;
import org.jruby.*;
import com.oracle.truffle.api.*;
@@ -138,7 +140,7 @@ public static String checkClassVariableName(RubyContext context, String name, Ru
}

public void load(Source source, RubyNode currentNode) {
execute(this, source, TranslatorDriver.ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null, currentNode);
execute(this, source, UTF8Encoding.INSTANCE, TranslatorDriver.ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null, currentNode);
}

public void loadFile(String fileName, RubyNode currentNode) {
@@ -163,7 +165,7 @@ private void loadFileAbsolute(String fileName, RubyNode currentNode) {
final Source source = Source.fromBytes(bytes, fileName, new BytesDecoder.UTF8BytesDecoder());

coreLibrary.getLoadedFeatures().slowPush(makeString(fileName));
execute(this, source, TranslatorDriver.ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null, currentNode);
execute(this, source, UTF8Encoding.INSTANCE, TranslatorDriver.ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null, currentNode);
}

public RubySymbol.SymbolTable getSymbolTable() {
@@ -180,27 +182,27 @@ public RubySymbol newSymbol(ByteList name) {
return symbolTable.getSymbol(name);
}

public Object eval(String code, RubyNode currentNode) {
public Object eval(ByteList code, RubyNode currentNode) {
final Source source = Source.fromText(code, "(eval)");
return execute(this, source, TranslatorDriver.ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null, currentNode);
return execute(this, source, code.getEncoding(), TranslatorDriver.ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null, currentNode);
}

public Object eval(String code, Object self, RubyNode currentNode) {
public Object eval(ByteList code, Object self, RubyNode currentNode) {
final Source source = Source.fromText(code, "(eval)");
return execute(this, source, TranslatorDriver.ParserContext.TOP_LEVEL, self, null, currentNode);
return execute(this, source, code.getEncoding(), TranslatorDriver.ParserContext.TOP_LEVEL, self, null, currentNode);
}

public Object eval(String code, RubyBinding binding, RubyNode currentNode) {
public Object eval(ByteList code, RubyBinding binding, RubyNode currentNode) {
final Source source = Source.fromText(code, "(eval)");
return execute(this, source, TranslatorDriver.ParserContext.TOP_LEVEL, binding.getSelf(), binding.getFrame(), currentNode);
return execute(this, source, code.getEncoding(), TranslatorDriver.ParserContext.TOP_LEVEL, binding.getSelf(), binding.getFrame(), currentNode);
}

public Object execute(RubyContext context, Source source, TranslatorDriver.ParserContext parserContext, Object self, MaterializedFrame parentFrame, RubyNode currentNode) {
return execute(context, source, parserContext, self, parentFrame, currentNode, NodeWrapper.IDENTITY);
public Object execute(RubyContext context, Source source, Encoding defaultEncoding, TranslatorDriver.ParserContext parserContext, Object self, MaterializedFrame parentFrame, RubyNode currentNode) {
return execute(context, source, defaultEncoding, parserContext, self, parentFrame, currentNode, NodeWrapper.IDENTITY);
}

public Object execute(RubyContext context, Source source, TranslatorDriver.ParserContext parserContext, Object self, MaterializedFrame parentFrame, RubyNode currentNode, NodeWrapper wrapper) {
final RubyRootNode rootNode = translator.parse(context, source, parserContext, parentFrame, currentNode, wrapper);
public Object execute(RubyContext context, Source source, Encoding defaultEncoding, TranslatorDriver.ParserContext parserContext, Object self, MaterializedFrame parentFrame, RubyNode currentNode, NodeWrapper wrapper) {
final RubyRootNode rootNode = translator.parse(context, source, defaultEncoding, parserContext, parentFrame, currentNode, wrapper);
final CallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);

// TODO(CS): we really need a method here - it's causing problems elsewhere
23 changes: 15 additions & 8 deletions core/src/main/java/org/jruby/truffle/runtime/RubyOperations.java
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
*/
package org.jruby.truffle.runtime;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.object.*;
import org.jruby.truffle.runtime.core.RubyBasicObject;

@@ -24,7 +25,8 @@ public RubyOperations(RubyContext context) {
this.context = context;
}

public void setInstanceVariable(RubyBasicObject receiver, String name, Object value) {
@CompilerDirectives.TruffleBoundary
public void setInstanceVariable(RubyBasicObject receiver, Object name, Object value) {
Shape shape = receiver.getDynamicObject().getShape();
Property property = shape.getProperty(name);
if (property != null) {
@@ -34,13 +36,15 @@ public void setInstanceVariable(RubyBasicObject receiver, String name, Object va
}
}

public void setInstanceVariables(RubyBasicObject receiver, Map<String, Object> instanceVariables) {
for (Map.Entry<String, Object> entry : instanceVariables.entrySet()) {
@CompilerDirectives.TruffleBoundary
public void setInstanceVariables(RubyBasicObject receiver, Map<Object, Object> instanceVariables) {
for (Map.Entry<Object, Object> entry : instanceVariables.entrySet()) {
setInstanceVariable(receiver, entry.getKey(), entry.getValue());
}
}

public Object getInstanceVariable(RubyBasicObject receiver, String name) {
@CompilerDirectives.TruffleBoundary
public Object getInstanceVariable(RubyBasicObject receiver, Object name) {
Shape shape = receiver.getDynamicObject().getShape();
Property property = shape.getProperty(name);
if (property != null) {
@@ -50,9 +54,10 @@ public Object getInstanceVariable(RubyBasicObject receiver, String name) {
}
}

public Map<String,Object> getInstanceVariables(RubyBasicObject receiver) {
@CompilerDirectives.TruffleBoundary
public Map<Object, Object> getInstanceVariables(RubyBasicObject receiver) {
Shape shape = receiver.getDynamicObject().getShape();
Map<String, Object> vars = new LinkedHashMap<>();
Map<Object, Object> vars = new LinkedHashMap<>();
List<Property> properties = shape.getPropertyList();
for (Property property : properties) {
if (property.getKey() != RubyBasicObject.OBJECT_ID_IDENTIFIER) {
@@ -62,10 +67,12 @@ public Map<String,Object> getInstanceVariables(RubyBasicObject receiver) {
return vars;
}

public String[] getFieldNames(RubyBasicObject receiver) {
return receiver.getDynamicObject().getShape().getKeyList().toArray(new String[0]);
@CompilerDirectives.TruffleBoundary
public Object[] getFieldNames(RubyBasicObject receiver) {
return receiver.getDynamicObject().getShape().getKeyList().toArray(new Object[0]);
}

@CompilerDirectives.TruffleBoundary
public boolean isFieldDefined(RubyBasicObject receiver, String name) {
return receiver.getDynamicObject().getShape().hasProperty(name);
}
20 changes: 12 additions & 8 deletions core/src/main/java/org/jruby/truffle/runtime/core/CoreLibrary.java
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@
import com.oracle.truffle.api.source.Source;
import org.jcodings.Encoding;
import org.jcodings.EncodingDB;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.runtime.Constants;
import org.jruby.runtime.encoding.EncodingService;
import org.jruby.truffle.nodes.RubyNode;
@@ -266,11 +267,11 @@ public void initialize() {
// Create the globals object

globalVariablesObject = new RubyBasicObject(objectClass);
globalVariablesObject.setInstanceVariable("$LOAD_PATH", new RubyArray(arrayClass));
globalVariablesObject.setInstanceVariable("$LOADED_FEATURES", new RubyArray(arrayClass));
globalVariablesObject.setInstanceVariable("$:", globalVariablesObject.getInstanceVariable("$LOAD_PATH"));
globalVariablesObject.setInstanceVariable("$\"", globalVariablesObject.getInstanceVariable("$LOADED_FEATURES"));
globalVariablesObject.setInstanceVariable("$,", nilObject);
globalVariablesObject.getOperations().setInstanceVariable(globalVariablesObject, "$LOAD_PATH", new RubyArray(arrayClass));
globalVariablesObject.getOperations().setInstanceVariable(globalVariablesObject, "$LOADED_FEATURES", new RubyArray(arrayClass));
globalVariablesObject.getOperations().setInstanceVariable(globalVariablesObject, "$:", globalVariablesObject.getInstanceVariable("$LOAD_PATH"));
globalVariablesObject.getOperations().setInstanceVariable(globalVariablesObject, "$\"", globalVariablesObject.getInstanceVariable("$LOADED_FEATURES"));
globalVariablesObject.getOperations().setInstanceVariable(globalVariablesObject, "$,", nilObject);

initializeEncodingConstants();

@@ -286,8 +287,11 @@ public void initialize() {
fileClass.setConstant(null, "PATH_SEPARATOR", RubyString.fromJavaString(stringClass, File.pathSeparator));
fileClass.setConstant(null, "FNM_SYSCASE", 0);

globalVariablesObject.setInstanceVariable("$DEBUG", context.getRuntime().isDebug());
globalVariablesObject.setInstanceVariable("$VERBOSE", context.getRuntime().warningsEnabled() ? context.getRuntime().isVerbose() : nilObject);
RubyNode.notDesignedForCompilation();
globalVariablesObject.getOperations().setInstanceVariable(globalVariablesObject, "$DEBUG", context.getRuntime().isDebug());
Object value = context.getRuntime().warningsEnabled() ? context.getRuntime().isVerbose() : nilObject;
RubyNode.notDesignedForCompilation();
globalVariablesObject.getOperations().setInstanceVariable(globalVariablesObject, "$VERBOSE", value);
}

public void initializeAfterMethodsAdded() {
@@ -313,7 +317,7 @@ public void loadRubyCore(String fileName) {
throw new RuntimeException(e);
}

context.execute(context, source, TranslatorDriver.ParserContext.TOP_LEVEL, mainObject, null, null);
context.execute(context, source, UTF8Encoding.INSTANCE, TranslatorDriver.ParserContext.TOP_LEVEL, mainObject, null, null);
}

public void initializeEncodingConstants() {
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@
public class RubyBasicObject {

public static final InternalName OBJECT_ID_IDENTIFIER = new InternalName("object_id");
public static final InternalName TAINTED_IDENTIFIER = new InternalName("tainted?");

public static Layout LAYOUT = Layout.createLayout(Layout.INT_TO_LONG);

@@ -107,11 +108,6 @@ public RubyClass getSingletonClass(Node currentNode) {
return metaClass;
}

public void setInstanceVariable(String name, Object value) {
RubyNode.notDesignedForCompilation();
getOperations().setInstanceVariable(this, name, value);
}

@CompilerDirectives.TruffleBoundary
public long getObjectID() {
// TODO(CS): we should specialise on reading this in the #object_id method and anywhere else it's used
@@ -126,24 +122,9 @@ public long getObjectID() {
return objectID;
}

@CompilerDirectives.TruffleBoundary
public void setInstanceVariables(Map<String, Object> instanceVariables) {
getOperations().setInstanceVariables(this, instanceVariables);
}


@CompilerDirectives.TruffleBoundary
public Map<String, Object> getInstanceVariables() {
return getOperations().getInstanceVariables(this);
}

public String[] getFieldNames() {
return getOperations().getFieldNames(this);
}

public void extend(RubyModule module, RubyNode currentNode) {
RubyNode.notDesignedForCompilation();

getSingletonClass(currentNode).include(currentNode, module);
}

Original file line number Diff line number Diff line change
@@ -121,7 +121,7 @@ public RubyModuleClass(RubyContext context) {

@Override
public RubyBasicObject newInstance(RubyNode currentNode) {
return new RubyModule(getContext(), null, "(unnamed module)");
return new RubyModule(getContext(), null, null);
}

}
43 changes: 31 additions & 12 deletions core/src/main/java/org/jruby/truffle/runtime/core/RubyRegexp.java
Original file line number Diff line number Diff line change
@@ -256,32 +256,48 @@ private void setFrame(Frame frame, String name, Object value) {
private void setThread(String name, Object value) {
assert value != null;

getContext().getThreadManager().getCurrentThread().getThreadLocals().setInstanceVariable(name, value);
RubyNode.notDesignedForCompilation();
getContext().getThreadManager().getCurrentThread().getThreadLocals().getOperations().setInstanceVariable(getContext().getThreadManager().getCurrentThread().getThreadLocals(), name, value);
}

@CompilerDirectives.TruffleBoundary
public RubyString gsub(String string, String replacement) {
public RubyString gsub(RubyString string, String replacement) {
final RubyContext context = getContext();

final byte[] stringBytes = string.getBytes(StandardCharsets.UTF_8);
final byte[] stringBytes = string.getBytes().bytes();

final Encoding encoding = string.getBytes().getEncoding();
final Matcher matcher = regex.matcher(stringBytes);

final StringBuilder builder = new StringBuilder();
int p = string.getBytes().getBegin();
int end = 0;
int range = p + string.getBytes().getRealSize();
int lastMatchEnd = 0;

int p = 0;
// We only ever care about the entire matched string, not each of the matched parts, so we can hard-code the index.
int matchedStringIndex = 0;

final StringBuilder builder = new StringBuilder();

while (true) {
final int match = matcher.search(p, stringBytes.length, Option.DEFAULT);
Object matchData = matchCommon(string.getBytes(), false, false, matcher, p + end, range);

if (matchData == context.getCoreLibrary().getNilObject()) {
builder.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(stringBytes, lastMatchEnd, range - lastMatchEnd)));

if (match == -1) {
builder.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(stringBytes, p, stringBytes.length - p)));
break;
} else {
builder.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(stringBytes, p, matcher.getBegin() - p)));
builder.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(replacement.getBytes(StandardCharsets.UTF_8))));
}

p = matcher.getEnd();
Region region = matcher.getEagerRegion();

int regionStart = region.beg[matchedStringIndex];
int regionEnd = region.end[matchedStringIndex];

builder.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(stringBytes, lastMatchEnd, regionStart - lastMatchEnd)));
builder.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(replacement.getBytes(StandardCharsets.UTF_8))));

lastMatchEnd = regionEnd;
end = StringSupport.positionEndForScan(string.getBytes(), matcher, encoding, p, range);
}

return context.makeString(builder.toString());
@@ -382,6 +398,9 @@ public Object scan(RubyString string) {
final List<RubyString> parts = new ArrayList<>();

Object[] values = md.getValues();

// The first element is the entire matched string, so skip over it because we're only interested in
// the constituent matched parts.
for (int i = 1; i < values.length; i++) {
parts.add((RubyString) values[i]);
}
64 changes: 48 additions & 16 deletions core/src/main/java/org/jruby/truffle/translator/BodyTranslator.java
Original file line number Diff line number Diff line change
@@ -1206,23 +1206,35 @@ public RubyNode visitGlobalVarNode(org.jruby.ast.GlobalVarNode node) {
public RubyNode visitHashNode(org.jruby.ast.HashNode node) {
final SourceSection sourceSection = translate(node.getPosition());

final List<RubyNode> hashConcats = new ArrayList<>();

final List<RubyNode> keyValues = new ArrayList<>();

for (KeyValuePair<Node, Node> pair: node.getPairs()) {
if (pair.getKey() == null) {
keyValues.add(new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getNilObject()));
final RubyNode hashLiteralSoFar = HashLiteralNode.create(context, translate(node.getPosition()), keyValues.toArray(new RubyNode[keyValues.size()]));
hashConcats.add(hashLiteralSoFar);
hashConcats.add(HashCastNodeFactory.create(context, sourceSection, pair.getValue().accept(this)));
keyValues.clear();
} else {
keyValues.add(pair.getKey().accept(this));
}

if (pair.getValue() == null) {
keyValues.add(new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getNilObject()));
} else {
keyValues.add(pair.getValue().accept(this));
if (pair.getValue() == null) {
keyValues.add(new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getNilObject()));
} else {
keyValues.add(pair.getValue().accept(this));
}
}
}

return HashLiteralNode.create(context, translate(node.getPosition()), keyValues.toArray(new RubyNode[keyValues.size()]));
if (hashConcats.size() == 1) {
return hashConcats.get(0);
}

final RubyNode hashLiteralSoFar = HashLiteralNode.create(context, translate(node.getPosition()), keyValues.toArray(new RubyNode[keyValues.size()]));
hashConcats.add(hashLiteralSoFar);

return new ConcatHashLiteralNode(context, sourceSection, hashConcats.toArray(new RubyNode[hashConcats.size()]));
}

@Override
@@ -1557,8 +1569,20 @@ public RubyNode visitMultipleAsgnNode(org.jruby.ast.MultipleAsgn19Node node) {
* a, b, c, = *x
*
* So we insert the splat cast node, even though it isn't there.
*
* In either case, we return the RHS
*/

final List<RubyNode> sequence = new ArrayList<>();

/*
* Store the RHS in a temp.
*/

final String tempRHSName = environment.allocateLocalTemp("rhs");
final RubyNode writeTempRHS = ((ReadNode) environment.findLocalVarNode(tempRHSName, sourceSection)).makeWriteNode(rhsTranslated);
sequence.add(writeTempRHS);

/*
* Create a temp for the array.
*/
@@ -1570,9 +1594,7 @@ public RubyNode visitMultipleAsgnNode(org.jruby.ast.MultipleAsgn19Node node) {
* the temp.
*/

final List<RubyNode> sequence = new ArrayList<>();

final RubyNode splatCastNode = SplatCastNodeFactory.create(context, sourceSection, translatingNextExpression ? SplatCastNode.NilBehavior.EMPTY_ARRAY : SplatCastNode.NilBehavior.ARRAY_WITH_NIL, rhsTranslated);
final RubyNode splatCastNode = ArrayCastNodeFactory.create(context, sourceSection, translatingNextExpression ? SplatCastNode.NilBehavior.EMPTY_ARRAY : SplatCastNode.NilBehavior.ARRAY_WITH_NIL, environment.findLocalVarNode(tempRHSName, sourceSection));

final RubyNode writeTemp = ((ReadNode) environment.findLocalVarNode(tempName, sourceSection)).makeWriteNode(splatCastNode);

@@ -1594,7 +1616,7 @@ public RubyNode visitMultipleAsgnNode(org.jruby.ast.MultipleAsgn19Node node) {
sequence.add(translateDummyAssignment(node.getRest(), assignedValue));
}

result = SequenceNode.sequence(context, sourceSection, sequence);
result = new ElidableResultNode(context, sourceSection, SequenceNode.sequence(context, sourceSection, sequence), environment.findLocalVarNode(tempRHSName, sourceSection));
} else if (node.getPre() == null
&& node.getPost() == null
&& node.getRest() instanceof org.jruby.ast.StarNode) {
@@ -2026,10 +2048,10 @@ public RubyNode visitRegexpNode(org.jruby.ast.RegexpNode node) {
if (node.getOptions().isEncodingNone()) {
// This isn't quite right - we shouldn't be looking up by name, we need a real reference to this constants

if (all7Bit(node.getValue().bytes())) {
regexp.forceEncoding((RubyEncoding) context.getCoreLibrary().getEncodingClass().getConstants().get("US_ASCII").getValue());
if (!all7Bit(node.getValue().bytes())) {
regexp.forceEncoding((RubyEncoding) context.getCoreLibrary().getEncodingClass().getConstants().get("ASCII_8BIT").getValue());
} else {
regexp.forceEncoding((RubyEncoding) context.getCoreLibrary().getEncodingClass().getConstants().get("ASCII-8BIT").getValue());
regexp.forceEncoding((RubyEncoding) context.getCoreLibrary().getEncodingClass().getConstants().get("US_ASCII").getValue());
}
} else if (node.getOptions().getKCode().getKCode().equals("SJIS")) {
regexp.forceEncoding((RubyEncoding) context.getCoreLibrary().getEncodingClass().getConstants().get("Windows_31J").getValue());
@@ -2048,11 +2070,21 @@ public RubyNode visitRegexpNode(org.jruby.ast.RegexpNode node) {
return literalNode;
}

private static boolean all7Bit(byte[] bytes) {
public static boolean all7Bit(byte[] bytes) {
for (int n = 0; n < bytes.length; n++) {
if (bytes[n] < 0 || bytes[n] > 0x7F) {
if (bytes[n] < 0 || bytes[n] > Byte.MAX_VALUE) {
return false;
}

if (bytes[n] == '\\' && n + 1 < bytes.length && bytes[n + 1] == 'x') {
int b = Integer.parseInt(new String(Arrays.copyOfRange(bytes, n + 2, n + 4)), 16);

if (b > 0x7F) {
return false;
}

n += 3;
}
}

return true;
Original file line number Diff line number Diff line change
@@ -118,10 +118,12 @@ public RubyNode visitArgsNode(org.jruby.ast.ArgsNode node) {

if (node.getPost() != null) {
state = State.POST;
index = -argsNode.getPostCount();
for (org.jruby.ast.Node arg : node.getPost().childNodes()) {
index = -1;
final List<org.jruby.ast.Node> children = new ArrayList<>(node.getPost().childNodes());
Collections.reverse(children);
for (org.jruby.ast.Node arg : children) {
sequence.add(arg.accept(this));
++index;
index--;
required++;
}
}
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@

import com.oracle.truffle.api.source.*;
import com.oracle.truffle.api.frame.*;
import org.jcodings.Encoding;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
@@ -68,7 +69,7 @@ public MethodDefinitionNode parse(RubyContext context, org.jruby.ast.Node parseT
return translator.compileFunctionNode(sourceSection, "(unknown)", argsNode, bodyNode, false, sharedMethod);
}

public RubyRootNode parse(RubyContext context, Source source, ParserContext parserContext, MaterializedFrame parentFrame, RubyNode currentNode, NodeWrapper wrapper) {
public RubyRootNode parse(RubyContext context, Source source, Encoding defaultEncoding, ParserContext parserContext, MaterializedFrame parentFrame, RubyNode currentNode, NodeWrapper wrapper) {
// Set up the JRuby parser

final org.jruby.parser.Parser parser = new org.jruby.parser.Parser(context.getRuntime());
@@ -97,6 +98,7 @@ public RubyRootNode parse(RubyContext context, Source source, ParserContext pars
}

final org.jruby.parser.ParserConfiguration parserConfiguration = new org.jruby.parser.ParserConfiguration(context.getRuntime(), 0, false, parserContext == ParserContext.TOP_LEVEL, true);
parserConfiguration.setDefaultEncoding(defaultEncoding);

// Parse to the JRuby AST

4 changes: 4 additions & 0 deletions core/src/main/ruby/jruby/truffle/core/kernel.rb
Original file line number Diff line number Diff line change
@@ -41,6 +41,10 @@ def printf(*args)
puts sprintf(*args)
end

alias_method :trust, :untaint
alias_method :untrust, :taint
alias_method :untrusted?, :tainted?

end

# Here temporarily
14 changes: 1 addition & 13 deletions spec/truffle/tags/core/string/gsub_tags.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
fails:String#gsub with pattern and replacement inserts the replacement around every character when the pattern collapses
fails:String#gsub with pattern and replacement respects $KCODE when the pattern collapses
fails:String#gsub with pattern and replacement doesn't freak out when replacing ^
fails:String#gsub with pattern and replacement returns a copy of self with all occurrences of pattern replaced with replacement
fails:String#gsub with pattern and replacement ignores a block if supplied
fails:String#gsub with pattern and replacement supports \G which matches at the beginning of the remaining (non-matched) string
fails:String#gsub with pattern and replacement supports /i for ignoring case
fails:String#gsub with pattern and replacement doesn't interpret regexp metacharacters if pattern is a string
fails:String#gsub with pattern and replacement replaces \1 sequences with the regexp's corresponding capture
fails:String#gsub with pattern and replacement treats \1 sequences without corresponding captures as empty strings
fails:String#gsub with pattern and replacement replaces \& and \0 with the complete match
@@ -14,11 +6,8 @@ fails:String#gsub with pattern and replacement replaces \' with everything after
fails:String#gsub with pattern and replacement replaces \+ with the last paren that actually matched
fails:String#gsub with pattern and replacement treats \+ as an empty string if there was no captures
fails:String#gsub with pattern and replacement maps \\ in replacement to \
fails:String#gsub with pattern and replacement leaves unknown \x escapes in replacement untouched
fails:String#gsub with pattern and replacement leaves \ at the end of replacement untouched
fails:String#gsub with pattern and replacement replaces \k named backreferences with the regexp's corresponding capture
fails:String#gsub with pattern and replacement taints the result if the original string or replacement is tainted
fails:String#gsub with pattern and replacement handles pattern collapse without $KCODE
fails:String#gsub with pattern and replacement untrusts the result if the original string or replacement is untrusted
fails:String#gsub with pattern and replacement tries to convert pattern to a string using to_str
fails:String#gsub with pattern and replacement raises a TypeError when pattern can't be converted to a string
@@ -58,8 +47,7 @@ fails:String#gsub with pattern and block returns a copy of self with all occurre
fails:String#gsub with pattern and block sets $~ for access from the block
fails:String#gsub with pattern and block restores $~ after leaving the block
fails:String#gsub with pattern and block sets $~ to MatchData of last match and nil when there's none for access from outside
fails:String#gsub with pattern and block doesn't interpolate special sequences like \1 for the block's return value
fails:String#gsub with pattern and block converts the block's return value to a string using to_s
passes:String#gsub with pattern and block converts the block's return value to a string using to_s
fails:String#gsub with pattern and block untrusts the result if the original string or replacement is untrusted
fails:String#gsub with pattern and block uses the compatible encoding if they are compatible
fails:String#gsub with pattern and block raises an Encoding::CompatibilityError if the encodings are not compatible
1 change: 0 additions & 1 deletion spec/truffle/tags/language/block_tags.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
fails:Block-local variables override shadowed variables from the outer scope
fails:Post-args with pattern matching extracts matched blocks with post arguments
fails:A block yielded a single Array assigns nil to unassigned required arguments
fails:A block yielded a single Array assigns elements to optional arguments
fails:A block yielded a single Array assgins elements to post arguments
2 changes: 0 additions & 2 deletions spec/truffle/tags/language/encoding_tags.txt

This file was deleted.

2 changes: 0 additions & 2 deletions spec/truffle/tags/language/hash_tags.txt

This file was deleted.

2 changes: 0 additions & 2 deletions spec/truffle/tags/language/magic_comment_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/language/module_tags.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
fails:Assigning an anonymous module to a constant sets the name of the module
fails:Assigning an anonymous module to a constant does not set the name of a module scoped by an anonymous module
fails:Assigning an anonymous module to a constant sets the name of contained modules when assigning a toplevel anonymous module
2 changes: 0 additions & 2 deletions spec/truffle/tags/language/regexp/encoding_tags.txt

This file was deleted.

2 changes: 1 addition & 1 deletion spec/truffle/tags/language/send_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
fails:Invoking a method with optional argument(s), expands an array to arguments grouped in parentheses
fails:Invoking a method expands an array to arguments grouped in parentheses which in turn takes rest arguments
fails:Invoking a method converts a final splatted explicit Hash to an Array
2 changes: 0 additions & 2 deletions spec/truffle/tags/language/string_tags.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
fails:Ruby character strings taints the result of interpolation when an interpolated value is tainted
fails:Ruby character strings untrusts the result of interpolation when an interpolated value is untrusted
fails:Ruby character strings Unicode escaping can be done with \u{} and one to six hex digits
fails:Ruby character strings Unicode escaping can be done with \u and four hex digits
fails:Ruby String interpolation creates a String having an Encoding compatible with all components
14 changes: 1 addition & 13 deletions spec/truffle/tags/language/variables_tags.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
fails:Multiple assignment with a single RHS value assigns a simple MLHS
fails:Multiple assignment with a single RHS value calls #to_ary to convert an Object RHS when assigning a simple MLHS
fails:Multiple assignment with a single RHS value calls #to_ary if it is private
fails:Multiple assignment with a single RHS value does not call #to_ary if #respond_to? returns false
fails:Multiple assignment with a single RHS value wraps the Object in an Array if #to_ary returns nil
fails:Multiple assignment with a single RHS value raises a TypeError of #to_ary does not return an Array
fails:Multiple assignment with a single RHS value does not call #to_a to convert an Object RHS when assigning a simple MLHS
fails:Multiple assignment with a single RHS value does not call #to_ary on an Array instance
fails:Multiple assignment with a single RHS value does not call #to_a on an Array instance
fails:Multiple assignment with a single RHS value returns the RHS when it is an Array
fails:Multiple assignment with a single RHS value returns the RHS when it is an Array subclass
fails:Multiple assignment with a single RHS value does not call #to_ary on an Array subclass instance
fails:Multiple assignment with a single RHS value does not call #to_a on an Array subclass instance
fails:Multiple assignment with a single RHS value assigns a single LHS splat
@@ -22,24 +17,17 @@ fails:Multiple assignment with a single RHS value assigns a MLHS with leading sp
fails:Multiple assignment with a single RHS value assigns a grouped LHS without splat
fails:Multiple assignment with a single RHS value assigns a grouped LHS with splats
fails:Multiple assignment with a single RHS value does not mutate a RHS Array
fails:Multiple assignment with a single RHS value assigns values from a RHS method call
fails:Multiple assignment with a single RHS value assigns values from a RHS method call with arguments
fails:Multiple assignment with a single RHS value calls #to_ary on the value returned by the method call
fails:Multiple assignment with a single RHS value assigns global variables
fails:Multiple assignment with a single RHS value assigns indexed elements
fails:Multiple assignment with a single splatted RHS value calls #to_a to convert nil to an empty Array
fails:Multiple assignment with a single splatted RHS value returns an Array when the splatted object is an Array subclass
fails:Multiple assignment with a single splatted RHS value does not call #to_a if #respond_to? returns false
fails:Multiple assignment with a single splatted RHS value raises a TypeError if #to_a does not return an Array
fails:Multiple assignment with a single splatted RHS value assigns a MLHS with leading splat
fails:Multiple assignment with a single splatted RHS value calls #to_a to convert an Object splat RHS when assigned to a simple MLHS
fails:Multiple assignment with a single splatted RHS value does not call #to_ary to convert an Object splat RHS when assigned to a simple MLHS
fails:Multiple assignment with a single splatted RHS value calls #to_a to convert an Object RHS with MLHS
fails:Multiple assignment with a single splatted RHS value assigns a grouped LHS without splats
fails:Multiple assignment with a single splatted RHS value assigns a grouped LHS with splats
fails:Multiple assignment with a single splatted RHS value does not mutate a RHS Array
fails:Multiple assignment with a MRHS value calls #to_a to convert a splatted Object as part of a MRHS with a splat MLHS
fails:Multiple assignment with a MRHS value does not call #to_ary to convert a splatted Object as part of a MRHS with a splat MRHS
fails:Multiple assignment with a MRHS value assigns a grouped LHS without splat from a simple Array
fails:Multiple assignment with a MRHS value assigns a grouped LHS without splat from nested Arrays
fails:Multiple assignment with a MRHS value assigns a grouped LHS with splats from nested Arrays
@@ -49,6 +37,6 @@ fails:Multiple assignment with a MRHS value calls #to_a to convert a splatted Ob
fails:Multiple assignment with a MRHS value calls #to_ary to convert a splatted Object when the position receiving the value is a multiple assignment
fails:Multiple assignment with a MRHS value does not call #to_ary to convert an Object when the position receiving the value is a rest variable
fails:Multiple assignment with a MRHS value does not call #to_ary to convert a splatted Object when the position receiving the value is a simple variable
fails:Multiple assignment with a MRHS value does not mutate the assigned Array
fails:Multiple assignment with a RHS assignment value does not mutate a RHS Array
fails:Multiple assignment with a single splatted RHS value does not call #to_a on an Array subclass
fails:Multiple assignment with a single RHS value returns the RHS when it is an Array subclass

0 comments on commit 587715a

Please sign in to comment.