Skip to content

Commit

Permalink
Showing 5 changed files with 115 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ public CompatibleQueryNode(RubyContext context, SourceSection sourceSection) {
@TruffleBoundary
@Specialization
public Object isCompatible(RubyString first, RubyString second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(first, second);
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(first.getCodeRangeable(), second.getCodeRangeable());

if (compatibleEncoding != null) {
return RubyEncoding.getEncoding(compatibleEncoding);
@@ -139,7 +139,7 @@ public Object isCompatible(RubySymbol first, RubyRegexp second) {
@TruffleBoundary
@Specialization
public Object isCompatible(RubyString first, RubySymbol second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(first, second);
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(first.getCodeRangeable(), second);

if (compatibleEncoding != null) {
return RubyEncoding.getEncoding(compatibleEncoding);
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@
public class StringGuards {

public static boolean isSingleByteOptimizable(RubyString string) {
return StringSupport.isSingleByteOptimizable(string, string.getByteList().getEncoding());
return StringSupport.isSingleByteOptimizable(string.getCodeRangeable(), string.getByteList().getEncoding());
}

public static boolean isAsciiCompatible(RubyString string) {
@@ -34,7 +34,7 @@ public static boolean isSingleByte(RubyString string) {
}

public static boolean isValidOr7BitEncoding(RubyString string) {
return string.isCodeRangeValid() || CodeRangeSupport.isCodeRangeAsciiOnly(string);
return string.isCodeRangeValid() || CodeRangeSupport.isCodeRangeAsciiOnly(string.getCodeRangeable());
}

public static boolean isFixedWidthEncoding(RubyString string) {
58 changes: 30 additions & 28 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@
import com.oracle.truffle.api.dsl.*;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.DynamicObjectFactory;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.source.SourceSection;
@@ -42,6 +43,7 @@
import org.joni.Matcher;
import org.joni.Option;
import org.jruby.Ruby;
import org.jruby.runtime.Helpers;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.cast.CmpIntNode;
import org.jruby.truffle.nodes.cast.CmpIntNodeGen;
@@ -97,19 +99,19 @@ public static void set(RubyString string, ByteList bytes) {
public static void forceEncoding(RubyString string, Encoding encoding) {
string.modify();
string.clearCodeRange();
StringSupport.associateEncoding(string, encoding);
StringSupport.associateEncoding(string.getCodeRangeable(), encoding);
string.clearCodeRange();
}

public static int length(RubyString string) {
if (CompilerDirectives.injectBranchProbability(
CompilerDirectives.FASTPATH_PROBABILITY,
StringSupport.isSingleByteOptimizable(string, string.getByteList().getEncoding()))) {
StringSupport.isSingleByteOptimizable(string.getCodeRangeable(), string.getByteList().getEncoding()))) {

return string.getByteList().getRealSize();

} else {
return StringSupport.strLengthFromRubyString(string);
return StringSupport.strLengthFromRubyString(string.getCodeRangeable());
}
}

@@ -127,7 +129,7 @@ public static int clampExclusiveIndex(RubyString string, int index) {

@TruffleBoundary
public static Encoding checkEncoding(RubyString string, CodeRangeable other, Node node) {
final Encoding encoding = StringSupport.areCompatible(string, other);
final Encoding encoding = StringSupport.areCompatible(string.getCodeRangeable(), other);

if (encoding == null) {
throw new RaiseException(
@@ -142,7 +144,7 @@ public static Encoding checkEncoding(RubyString string, CodeRangeable other, Nod
}

public static boolean singleByteOptimizable(RubyString string) {
return StringSupport.isSingleByteOptimizable(string, EncodingUtils.STR_ENC_GET(string));
return StringSupport.isSingleByteOptimizable(string.getCodeRangeable(), EncodingUtils.STR_ENC_GET(string.getCodeRangeable()));
}

public static RubyString createEmptyString(RubyClass stringClass) {
@@ -188,7 +190,7 @@ public AddNode(RubyContext context, SourceSection sourceSection) {

@Specialization
public RubyString add(RubyString string, RubyString other) {
final Encoding enc = checkEncoding(string, other, this);
final Encoding enc = checkEncoding(string, other.getCodeRangeable(), this);
final RubyString ret = createString(StringSupport.addByteLists(string.getByteList(), other.getByteList()));

if (taintResultNode == null) {
@@ -311,7 +313,7 @@ public int compare(RubyString a, RubyString b) {

final int ret = a.getByteList().cmp(b.getByteList());

if ((ret == 0) && !StringSupport.areComparable(a, b)) {
if ((ret == 0) && !StringSupport.areComparable(a.getCodeRangeable(), b.getCodeRangeable())) {
return a.getByteList().getEncoding().getIndex() > b.getByteList().getEncoding().getIndex() ? 1 : -1;
}

@@ -427,7 +429,7 @@ public RubyString concat(RubyString string, RubyString other) {
final int[] ptr_cr_ret = { codeRange };

try {
EncodingUtils.encCrStrBufCat(getContext().getRuntime(), string, other.getByteList(), other.getByteList().getEncoding(), codeRange, ptr_cr_ret);
EncodingUtils.encCrStrBufCat(getContext().getRuntime(), string.getCodeRangeable(), other.getByteList(), other.getByteList().getEncoding(), codeRange, ptr_cr_ret);
} catch (org.jruby.exceptions.RaiseException e) {
if (e.getException().getMetaClass() == getContext().getRuntime().getEncodingCompatibilityError()) {
CompilerDirectives.transferToInterpreter();
@@ -753,7 +755,7 @@ public CaseCmpNode(RubyContext context, SourceSection sourceSection) {
public Object caseCmpSingleByte(RubyString string, RubyString other) {
// Taken from org.jruby.RubyString#casecmp19.

if (StringSupport.areCompatible(string, other) == null) {
if (StringSupport.areCompatible(string.getCodeRangeable(), other.getCodeRangeable()) == null) {
return nil();
}

@@ -764,7 +766,7 @@ public Object caseCmpSingleByte(RubyString string, RubyString other) {
public Object caseCmp(RubyString string, RubyString other) {
// Taken from org.jruby.RubyString#casecmp19 and

final Encoding encoding = StringSupport.areCompatible(string, other);
final Encoding encoding = StringSupport.areCompatible(string.getCodeRangeable(), other.getCodeRangeable());

if (encoding == null) {
return nil();
@@ -779,8 +781,8 @@ private int multiByteCasecmp(Encoding enc, ByteList value, ByteList otherValue)
}

public static boolean bothSingleByteOptimizable(RubyString string, RubyString other) {
final boolean stringSingleByteOptimizable = StringSupport.isSingleByteOptimizable(string, string.getByteList().getEncoding());
final boolean otherSingleByteOptimizable = StringSupport.isSingleByteOptimizable(other, other.getByteList().getEncoding());
final boolean stringSingleByteOptimizable = StringSupport.isSingleByteOptimizable(string.getCodeRangeable(), string.getByteList().getEncoding());
final boolean otherSingleByteOptimizable = StringSupport.isSingleByteOptimizable(other.getCodeRangeable(), other.getByteList().getEncoding());

return stringSingleByteOptimizable && otherSingleByteOptimizable;
}
@@ -815,7 +817,7 @@ public Object chopBang(VirtualFrame frame, RubyString string) {

@TruffleBoundary
private int choppedLength(RubyString string) {
return StringSupport.choppedLength19(string, getContext().getRuntime());
return StringSupport.choppedLength19(string.getCodeRangeable(), getContext().getRuntime());
}
}

@@ -859,7 +861,7 @@ private int countSlow(RubyString string, RubyString[] otherStrings) {
for (int i = 1; i < otherStrings.length; i++) {
otherStr = otherStrings[i];

enc = checkEncoding(string, otherStr, this);
enc = checkEncoding(string, otherStr.getCodeRangeable(), this);
tables = StringSupport.trSetupTable(otherStr.getByteList(), getContext().getRuntime(), table, tables, false, enc);
}

@@ -893,7 +895,7 @@ public Object crypt(RubyString string, RubyString salt) {
final RubyString otherStr = StringNodes.createString(getContext().getCoreLibrary().getStringClass(), otherBL);

otherStr.modify();
StringSupport.associateEncoding(otherStr, ascii8bit);
StringSupport.associateEncoding(otherStr.getCodeRangeable(), ascii8bit);

if (otherBL.length() < 2) {
CompilerDirectives.transferToInterpreter();
@@ -919,7 +921,7 @@ public Object crypt(RubyString string, RubyString salt) {
}

final RubyString result = StringNodes.createString(getContext().getCoreLibrary().getStringClass(), new ByteList(cryptedString, 0, cryptedString.length - 1));
StringSupport.associateEncoding(result, ascii8bit);
StringSupport.associateEncoding(result.getCodeRangeable(), ascii8bit);

return result;
}
@@ -973,19 +975,19 @@ public Object deleteBang(VirtualFrame frame, RubyString string, Object... args)
@TruffleBoundary
private Object deleteBangSlow(RubyString string, RubyString... otherStrings) {
RubyString otherString = otherStrings[0];
Encoding enc = checkEncoding(string, otherString, this);
Encoding enc = checkEncoding(string, otherString.getCodeRangeable(), this);

boolean[] squeeze = new boolean[StringSupport.TRANS_SIZE + 1];
StringSupport.TrTables tables = StringSupport.trSetupTable(otherString.getByteList(),
getContext().getRuntime(),
squeeze, null, true, enc);

for (int i = 1; i < otherStrings.length; i++) {
enc = checkEncoding(string, otherStrings[i], this);
enc = checkEncoding(string, otherStrings[i].getCodeRangeable(), this);
tables = StringSupport.trSetupTable(otherStrings[i].getByteList(), getContext().getRuntime(), squeeze, tables, false, enc);
}

if (StringSupport.delete_bangCommon19(string, getContext().getRuntime(), squeeze, tables, enc) == null) {
if (StringSupport.delete_bangCommon19(string.getCodeRangeable(), getContext().getRuntime(), squeeze, tables, enc) == null) {
return nil();
}

@@ -1396,7 +1398,7 @@ public Object lstripBang(RubyString string) {
return nil();
}

final Encoding enc = EncodingUtils.STR_ENC_GET(string);
final Encoding enc = EncodingUtils.STR_ENC_GET(string.getCodeRangeable());
final int s = string.getByteList().getBegin();
final int end = s + string.getByteList().getRealSize();
final byte[]bytes = string.getByteList().getUnsafeBytes();
@@ -1557,7 +1559,7 @@ public Object rstripBang(RubyString string) {
return nil();
}

final Encoding enc = EncodingUtils.STR_ENC_GET(string);
final Encoding enc = EncodingUtils.STR_ENC_GET(string.getCodeRangeable());
final byte[] bytes = string.getByteList().getUnsafeBytes();
final int start = string.getByteList().getBegin();
final int end = start + string.getByteList().getRealSize();
@@ -1621,7 +1623,7 @@ public RubyBasicObject swapcaseSingleByte(RubyString string) {
final int end = s + value.getRealSize();
final byte[]bytes = value.getUnsafeBytes();

if (singleByteOptimizableProfile.profile(StringSupport.isSingleByteOptimizable(string, enc))) {
if (singleByteOptimizableProfile.profile(StringSupport.isSingleByteOptimizable(string.getCodeRangeable(), enc))) {
if (StringSupport.singleByteSwapcase(bytes, s, end)) {
return string;
}
@@ -1835,7 +1837,7 @@ public int sizeSingleByte(RubyString string) {

@Specialization(guards = "!isSingleByteOptimizable(string)")
public int size(RubyString string) {
return StringSupport.strLengthFromRubyString(string);
return StringSupport.strLengthFromRubyString(string.getCodeRangeable());
}
}

@@ -1896,15 +1898,15 @@ public Object squeezeBang(VirtualFrame frame, RubyString string, Object... args)
}

RubyString otherStr = otherStrings[0];
Encoding enc = checkEncoding(string, otherStr, this);
Encoding enc = checkEncoding(string, otherStr.getCodeRangeable(), this);
final boolean squeeze[] = new boolean[StringSupport.TRANS_SIZE + 1];
StringSupport.TrTables tables = StringSupport.trSetupTable(otherStr.getByteList(), getContext().getRuntime(), squeeze, null, true, enc);

boolean singlebyte = singleByteOptimizable(string) && singleByteOptimizable(otherStr);

for (int i = 1; i < otherStrings.length; i++) {
otherStr = otherStrings[i];
enc = string.checkEncoding(otherStr);
enc = string.checkEncoding(otherStr.getCodeRangeable());
singlebyte = singlebyte && singleByteOptimizable(otherStr);
tables = StringSupport.trSetupTable(otherStr.getByteList(), getContext().getRuntime(), squeeze, tables, false, enc);
}
@@ -2470,18 +2472,18 @@ public static int checkIndexForRef(RubyString string, int index, RubyNode node)

@TruffleBoundary
public static void replaceInternal(RubyString string, int start, int length, RubyString replacement) {
StringSupport.replaceInternal19(start, length, string, replacement);
StringSupport.replaceInternal19(start, length, string.getCodeRangeable(), replacement.getCodeRangeable());
}

@TruffleBoundary
private static Object trTransHelper(RubyContext context, RubyString self, RubyString fromStr, RubyString toStr, boolean sFlag) {
final CodeRangeable ret = StringSupport.trTransHelper(context.getRuntime(), self, fromStr, toStr, sFlag);
final CodeRangeable ret = StringSupport.trTransHelper(context.getRuntime(), self.getCodeRangeable(), fromStr.getCodeRangeable(), toStr.getCodeRangeable(), sFlag);

if (ret == null) {
return context.getCoreLibrary().getNilObject();
}

return ret;
return self;
}
}

Original file line number Diff line number Diff line change
@@ -138,7 +138,7 @@ public RubyArray stringAwkSplit(RubyString string, int lim) {
boolean skip = true;

int e = 0, b = 0;
final boolean singlebyte = StringSupport.isSingleByteOptimizable(string, enc);
final boolean singlebyte = StringSupport.isSingleByteOptimizable(string.getCodeRangeable(), enc);
while (p < end) {
final int c;
if (singlebyte) {
@@ -445,7 +445,7 @@ public boolean stringEqual(RubyString string, RubyString other) {
final ByteList b = other.getByteList();

if (incompatibleEncodingProfile.profile((a.getEncoding() != b.getEncoding()) &&
(org.jruby.RubyEncoding.areCompatible(string, other) == null))) {
(org.jruby.RubyEncoding.areCompatible(string.getCodeRangeable(), other.getCodeRangeable()) == null))) {
return false;
}

@@ -610,8 +610,8 @@ public StringIndexPrimitiveNode(RubyContext context, SourceSection sourceSection

@Specialization
public Object stringIndex(RubyString string, RubyString pattern, int start) {
final int index = StringSupport.index(string,
pattern,
final int index = StringSupport.index(string.getCodeRangeable(),
pattern.getCodeRangeable(),
start, string.getByteList().getEncoding());

if (index == -1) {
@@ -720,7 +720,7 @@ public Object stringCharacterIndex(RubyString string, RubyString pattern, int of
final byte[] stringBytes = string.getByteList().getUnsafeBytes();
final byte[] patternBytes = pattern.getByteList().getUnsafeBytes();

if (StringSupport.isSingleByteOptimizable(string, string.getByteList().getEncoding())) {
if (StringSupport.isSingleByteOptimizable(string.getCodeRangeable(), string.getByteList().getEncoding())) {
for(s = p += offset, ss = pp; p < e; s = ++p) {
if (stringBytes[p] != patternBytes[pp]) continue;

@@ -854,7 +854,7 @@ public Object stringByteIndex(RubyString string, RubyString pattern, int offset)
return nil();
}

final Encoding encoding = StringNodes.checkEncoding(string, pattern, this);
final Encoding encoding = StringNodes.checkEncoding(string, pattern.getCodeRangeable(), this);
int p = string.getByteList().getBegin();
final int e = p + string.getByteList().getRealSize();
int pp = pattern.getByteList().getBegin();
@@ -1204,12 +1204,12 @@ public Object stringSubstring(RubyString string, int beg, int len) {
}
return makeSubstring(string, p - s, e - p);
} else {
beg += StringSupport.strLengthFromRubyString(string, enc);
beg += StringSupport.strLengthFromRubyString(string.getCodeRangeable(), enc);
if (beg < 0) {
return nil();
}
}
} else if (beg > 0 && beg > StringSupport.strLengthFromRubyString(string, enc)) {
} else if (beg > 0 && beg > StringSupport.strLengthFromRubyString(string.getCodeRangeable(), enc)) {
return nil();
}
if (len == 0) {
Original file line number Diff line number Diff line change
@@ -17,11 +17,13 @@
import org.jruby.util.CodeRangeable;
import org.jruby.util.StringSupport;

public class RubyString extends RubyBasicObject implements CodeRangeable {
public class RubyString extends RubyBasicObject {

public ByteList bytes;
public int codeRange = StringSupport.CR_UNKNOWN;

private CodeRangeableWrapper codeRangeableWrapper;

public RubyString(RubyClass stringClass, ByteList bytes, DynamicObject dynamicObject) {
super(stringClass, dynamicObject);
this.bytes = bytes;
@@ -33,12 +35,10 @@ public String toString() {
return Helpers.decodeByteList(getContext().getRuntime(), bytes);
}

@Override
public int getCodeRange() {
return codeRange;
}

@Override
@TruffleBoundary
public int scanForCodeRange() {
int cr = getCodeRange();
@@ -51,51 +51,43 @@ public int scanForCodeRange() {
return cr;
}

@Override
public boolean isCodeRangeValid() {
return codeRange == StringSupport.CR_VALID;
}

@Override
public final void setCodeRange(int codeRange) {
this.codeRange = codeRange;
public final void setCodeRange(int newCodeRange) {
codeRange = newCodeRange;
}

@Override
public final void clearCodeRange() {
codeRange = StringSupport.CR_UNKNOWN;
}

@Override
public final void keepCodeRange() {
if (getCodeRange() == StringSupport.CR_BROKEN) {
clearCodeRange();
}
}

@Override
public final void modify() {
// TODO (nirvdrum 16-Feb-15): This should check whether the underlying ByteList is being shared and copy if necessary.
bytes.invalidate();
}

@Override
public final void modify(int length) {
// TODO (nirvdrum Jan. 13, 2015): This should check whether the underlying ByteList is being shared and copy if necessary.
bytes.ensure(length);
bytes.invalidate();
}

@Override
public final void modifyAndKeepCodeRange() {
modify();
keepCodeRange();
}

@Override
@TruffleBoundary
public Encoding checkEncoding(CodeRangeable other) {
final Encoding encoding = StringSupport.areCompatible(this, other);
final Encoding encoding = StringSupport.areCompatible(getCodeRangeable(), other);

// TODO (nirvdrum 23-Mar-15) We need to raise a proper Truffle+JRuby exception here, rather than a non-Truffle JRuby exception.
if (encoding == null) {
@@ -108,7 +100,6 @@ public Encoding checkEncoding(CodeRangeable other) {
return encoding;
}

@Override
public ByteList getByteList() {
return bytes;
}
@@ -118,4 +109,71 @@ private int slowCodeRangeScan() {
return StringSupport.codeRangeScan(bytes.getEncoding(), bytes);
}

public CodeRangeableWrapper getCodeRangeable() {
if (codeRangeableWrapper == null) {
codeRangeableWrapper = new CodeRangeableWrapper();
}

return codeRangeableWrapper;
}

public class CodeRangeableWrapper implements CodeRangeable {

@Override
public int getCodeRange() {
return RubyString.this.getCodeRange();
}

@Override
public int scanForCodeRange() {
return RubyString.this.scanForCodeRange();
}

@Override
public boolean isCodeRangeValid() {
return RubyString.this.isCodeRangeValid();
}

@Override
public final void setCodeRange(int newCodeRange) {
RubyString.this.setCodeRange(newCodeRange);
}

@Override
public final void clearCodeRange() {
RubyString.this.clearCodeRange();
}

@Override
public final void keepCodeRange() {
RubyString.this.keepCodeRange();
}

@Override
public final void modify() {
RubyString.this.modify();
}

@Override
public final void modify(int length) {
RubyString.this.modify(length);
}

@Override
public final void modifyAndKeepCodeRange() {
RubyString.this.modifyAndKeepCodeRange();
}

@Override
public Encoding checkEncoding(CodeRangeable other) {
return RubyString.this.checkEncoding(other);
}

@Override
public ByteList getByteList() {
return RubyString.this.getByteList();
}

}

}

0 comments on commit 49b4402

Please sign in to comment.