Skip to content

Commit a5e17aa

Browse files
authored
Merge pull request #91 from bulasevich/GR-54313
[Backport] [GR-54313] TruffleString: fix TruffleStringBuilder and RepeatNode breaking string compaction invariants
2 parents 9f9548c + f418263 commit a5e17aa

File tree

3 files changed

+70
-40
lines changed

3 files changed

+70
-40
lines changed

truffle/src/com.oracle.truffle.api.strings.test/src/com/oracle/truffle/api/strings/test/builder/TStringBuilderAppendSubStringTest.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,28 @@ public static Iterable<TruffleStringBuilder.AppendSubstringByteIndexNode> data()
6868
public void testAll() throws Exception {
6969
forAllStrings(true, (a, arrayA, codeRangeA, isValidA, encodingA, codepointsA, byteIndicesA) -> {
7070
forAllStrings(new TruffleString.Encoding[]{encodingA}, true, (b, arrayB, codeRangeB, isValidB, encodingB, codepointsB, byteIndicesB) -> {
71-
TruffleStringBuilder sb = TruffleStringBuilder.create(encodingA);
72-
node.execute(sb, a, 0, arrayA.length);
73-
int fromByteIndexB = codepointsB.length > 1 ? byteIndicesB[1] : 0;
74-
int byteLengthB = (codepointsB.length > 2 ? byteIndicesB[codepointsB.length - 1] : arrayB.length) - fromByteIndexB;
75-
assert byteLengthB > 0;
76-
node.execute(sb, b, fromByteIndexB, byteLengthB);
77-
byte[] expected = Arrays.copyOf(arrayA, arrayA.length + byteLengthB);
78-
System.arraycopy(arrayB, fromByteIndexB, expected, arrayA.length, byteLengthB);
79-
assertBytesEqual(sb.toStringUncached(), encodingA, expected);
71+
forAllStrings(new TruffleString.Encoding[]{encodingA}, true, (c, arrayC, codeRangeC, isValidC, encodingC, codepointsC, byteIndicesC) -> {
72+
73+
TruffleStringBuilder sb = TruffleStringBuilder.create(encodingA);
74+
node.execute(sb, a, 0, arrayA.length);
75+
int fromByteIndexB = codepointsB.length > 1 ? byteIndicesB[1] : 0;
76+
int byteLengthB = (codepointsB.length > 2 ? byteIndicesB[codepointsB.length - 1] : arrayB.length) - fromByteIndexB;
77+
assert byteLengthB > 0;
78+
node.execute(sb, b, fromByteIndexB, byteLengthB);
79+
byte[] expected = Arrays.copyOf(arrayA, arrayA.length + byteLengthB);
80+
System.arraycopy(arrayB, fromByteIndexB, expected, arrayA.length, byteLengthB);
81+
assertBytesEqual(sb.toStringUncached(), encodingA, expected);
82+
83+
sb = TruffleStringBuilder.create(encodingA);
84+
node.execute(sb, a, 0, arrayA.length);
85+
TruffleString concat = b.concatUncached(c, encodingA, true);
86+
int fromByteIndex = b.byteLength(encodingA);
87+
int byteLength = c.byteLength(encodingA);
88+
node.execute(sb, concat, fromByteIndex, byteLength);
89+
expected = Arrays.copyOf(arrayA, arrayA.length + byteLength);
90+
System.arraycopy(arrayC, 0, expected, arrayA.length, byteLength);
91+
assertBytesEqual(sb.toStringUncached(), encodingA, expected);
92+
});
8093
});
8194
});
8295
}

truffle/src/com.oracle.truffle.api.strings/src/com/oracle/truffle/api/strings/TruffleString.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ private static boolean attrsAreCorrect(Object bytes, Encoding encoding, int offs
216216
}
217217
if (bytes instanceof NativePointer) {
218218
((NativePointer) bytes).materializeByteArray(null, offset, length << stride, InlinedConditionProfile.getUncached());
219+
} else {
220+
assert stride == Stride.fromCodeRangeAllowImprecise(codeRange, encoding);
219221
}
220222
long attrs = CalcStringAttributesNodeGen.getUncached().execute(CalcStringAttributesNodeGen.getUncached(), null, bytes, offset, length, stride, encoding, 0, knownCodeRange);
221223
int cpLengthCalc = StringAttributes.getCodePointLength(attrs);
@@ -4942,7 +4944,8 @@ final TruffleString repeat(AbstractTruffleString a, int n, Encoding expectedEnco
49424944
@Cached TStringInternalNodes.GetCodePointLengthNode getCodePointLengthNode,
49434945
@Cached TStringInternalNodes.CalcStringAttributesNode calcStringAttributesNode,
49444946
@Cached InlinedConditionProfile brokenProfile,
4945-
@Cached InlinedBranchProfile outOfMemoryProfile) {
4947+
@Cached InlinedBranchProfile outOfMemoryProfile,
4948+
@Cached InlinedBranchProfile compactProfile) {
49464949
a.checkEncoding(expectedEncoding);
49474950
if (n < 0) {
49484951
throw InternalErrors.illegalArgument("n must be positive");
@@ -4957,27 +4960,38 @@ final TruffleString repeat(AbstractTruffleString a, int n, Encoding expectedEnco
49574960
int codeRangeA = getPreciseCodeRangeNode.execute(this, a, expectedEncoding);
49584961
int codePointLengthA = getCodePointLengthNode.execute(this, a, expectedEncoding);
49594962
int byteLengthA = (a.length()) << a.stride();
4960-
long byteLength = ((long) byteLengthA) * n;
4963+
int stride = Stride.fromCodeRange(codeRangeA, expectedEncoding);
4964+
long byteLength = (((long) a.length()) << stride) * n;
49614965
if (Long.compareUnsigned(byteLength, TStringConstants.MAX_ARRAY_SIZE) > 0) {
49624966
outOfMemoryProfile.enter(this);
49634967
throw InternalErrors.outOfMemory();
49644968
}
49654969
byte[] array = new byte[(int) byteLength];
49664970
int offsetB = 0;
4967-
for (int i = 0; i < n; i++) {
4968-
TStringOps.arraycopyWithStride(this, arrayA, a.offset(), 0, 0, array, offsetB, 0, 0, byteLengthA);
4969-
offsetB += byteLengthA;
4970-
TStringConstants.truffleSafePointPoll(this, i + 1);
4971+
if (stride == a.stride()) {
4972+
for (int i = 0; i < n; i++) {
4973+
TStringOps.arraycopyWithStride(this, arrayA, a.offset(), 0, 0, array, offsetB, 0, 0, byteLengthA);
4974+
offsetB += byteLengthA;
4975+
TStringConstants.truffleSafePointPoll(this, i + 1);
4976+
}
4977+
} else {
4978+
compactProfile.enter(this);
4979+
int byteLengthCompact = a.length() << stride;
4980+
for (int i = 0; i < n; i++) {
4981+
TStringOps.arraycopyWithStride(this, arrayA, a.offset(), a.stride(), 0, array, offsetB, stride, 0, a.length());
4982+
offsetB += byteLengthCompact;
4983+
TStringConstants.truffleSafePointPoll(this, i + 1);
4984+
}
49714985
}
4972-
int length = (int) (byteLength >> a.stride());
4986+
int length = (int) (byteLength >> stride);
49734987
if (brokenProfile.profile(this, isBroken(codeRangeA))) {
4974-
long attrs = calcStringAttributesNode.execute(this, null, array, 0, length, a.stride(), expectedEncoding, 0, codeRangeA);
4988+
long attrs = calcStringAttributesNode.execute(this, null, array, 0, length, stride, expectedEncoding, 0, codeRangeA);
49754989
codeRangeA = StringAttributes.getCodeRange(attrs);
49764990
codePointLengthA = StringAttributes.getCodePointLength(attrs);
49774991
} else {
49784992
codePointLengthA *= n;
49794993
}
4980-
return createFromByteArray(array, length, a.stride(), expectedEncoding, codePointLengthA, codeRangeA);
4994+
return createFromByteArray(array, length, stride, expectedEncoding, codePointLengthA, codeRangeA);
49814995
}
49824996

49834997
/**

truffle/src/com.oracle.truffle.api.strings/src/com/oracle/truffle/api/strings/TruffleStringBuilder.java

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,7 @@ final void append(TruffleStringBuilderUTF8 sb, AbstractTruffleString a, int from
10981098
@Specialization
10991099
final void append(TruffleStringBuilderUTF16 sb, AbstractTruffleString a, int fromByteIndex, int byteLength,
11001100
@Cached @Shared TruffleString.ToIndexableNode toIndexableNode,
1101+
@Cached @Shared TStringInternalNodes.GetPreciseCodeRangeNode getPreciseCodeRangeNode,
11011102
@Cached @Shared InlinedBranchProfile slowPathProfile,
11021103
@Cached @Shared InlinedBranchProfile inflateProfile,
11031104
@Cached @Shared InlinedBranchProfile bufferGrowProfile,
@@ -1120,30 +1121,32 @@ final void append(TruffleStringBuilderUTF16 sb, AbstractTruffleString a, int fro
11201121
sb.codePointLength += length;
11211122
} else {
11221123
slowPathProfile.enter(this);
1123-
final int codeRangeA = a.codeRange();
11241124
final int codePointLength;
11251125
final int codeRange;
11261126
if (a.stride() == 0) {
1127-
codeRange = TSCodeRange.markImprecise(codeRangeA);
1127+
codeRange = TSCodeRange.markImprecise(a.codeRange());
11281128
codePointLength = length;
1129-
} else if (fromIndex == 0 && length == a.length()) {
1130-
codeRange = codeRangeA;
1131-
codePointLength = a.codePointLength();
1132-
} else if (TSCodeRange.is16Bit(codeRangeA)) {
1133-
assert a.stride() == 1;
1134-
codeRange = TStringOps.calcStringAttributesBMP(this, arrayA, a.offset() + fromByteIndex, length);
1135-
codePointLength = length;
1136-
} else if (TSCodeRange.isValidMultiByte(codeRangeA)) {
1137-
long attrs = TStringOps.calcStringAttributesUTF16(this, arrayA, a.offset() + fromByteIndex, length, true);
1138-
codeRange = StringAttributes.getCodeRange(attrs);
1139-
codePointLength = StringAttributes.getCodePointLength(attrs);
11401129
} else {
1141-
long attrs = TStringOps.calcStringAttributesUTF16(this, arrayA, a.offset() + fromByteIndex, length, false);
1142-
codeRange = StringAttributes.getCodeRange(attrs);
1143-
codePointLength = StringAttributes.getCodePointLength(attrs);
1130+
final int codeRangeA = getPreciseCodeRangeNode.execute(this, a, Encoding.UTF_16);
1131+
if (fromIndex == 0 && length == a.length()) {
1132+
codeRange = codeRangeA;
1133+
codePointLength = a.codePointLength();
1134+
} else if (TSCodeRange.is16Bit(codeRangeA)) {
1135+
assert a.stride() == 1;
1136+
codeRange = TStringOps.calcStringAttributesBMP(this, arrayA, a.offset() + fromByteIndex, length);
1137+
codePointLength = length;
1138+
} else if (TSCodeRange.isValidMultiByte(codeRangeA)) {
1139+
long attrs = TStringOps.calcStringAttributesUTF16(this, arrayA, a.offset() + fromByteIndex, length, true);
1140+
codeRange = StringAttributes.getCodeRange(attrs);
1141+
codePointLength = StringAttributes.getCodePointLength(attrs);
1142+
} else {
1143+
long attrs = TStringOps.calcStringAttributesUTF16(this, arrayA, a.offset() + fromByteIndex, length, false);
1144+
codeRange = StringAttributes.getCodeRange(attrs);
1145+
codePointLength = StringAttributes.getCodePointLength(attrs);
1146+
}
11441147
}
11451148
sb.updateCodeRange(codeRange);
1146-
sb.ensureCapacityAndInflate(this, a.length(), Stride.fromCodeRangeUTF16AllowImprecise(codeRangeA), inflateProfile, bufferGrowProfile, errorProfile);
1149+
sb.ensureCapacityAndInflate(this, a.length(), Stride.fromCodeRangeUTF16AllowImprecise(codeRange), inflateProfile, bufferGrowProfile, errorProfile);
11471150
TStringOps.arraycopyWithStride(this,
11481151
arrayA, a.offset(), a.stride(), fromIndex,
11491152
sb.buf, 0, sb.stride, sb.length, length);
@@ -1155,6 +1158,7 @@ final void append(TruffleStringBuilderUTF16 sb, AbstractTruffleString a, int fro
11551158
@Specialization
11561159
final void append(TruffleStringBuilderUTF32 sb, AbstractTruffleString a, int fromByteIndex, int byteLength,
11571160
@Cached @Shared TruffleString.ToIndexableNode toIndexableNode,
1161+
@Cached @Shared TStringInternalNodes.GetPreciseCodeRangeNode getPreciseCodeRangeNode,
11581162
@Cached @Shared InlinedBranchProfile slowPathProfile,
11591163
@Cached @Shared InlinedBranchProfile inflateProfile,
11601164
@Cached @Shared InlinedBranchProfile bufferGrowProfile,
@@ -1175,18 +1179,17 @@ final void append(TruffleStringBuilderUTF32 sb, AbstractTruffleString a, int fro
11751179
sb.buf, 0, 0, sb.length, length);
11761180
} else {
11771181
slowPathProfile.enter(this);
1178-
final int codeRangeA = a.codeRange();
11791182
final int codeRange;
1180-
if (a.stride() == 0 || fromIndex == 0 && length == a.length() || !TSCodeRange.isMoreGeneralThan(codeRangeA, sb.codeRange)) {
1181-
codeRange = TSCodeRange.markImprecise(codeRangeA);
1183+
if (a.stride() == 0 || fromIndex == 0 && length == a.length() || !TSCodeRange.isMoreGeneralThan(getPreciseCodeRangeNode.execute(this, a, Encoding.UTF_32), sb.codeRange)) {
1184+
codeRange = TSCodeRange.markImprecise(a.codeRange());
11821185
} else if (a.stride() == 1) {
11831186
codeRange = TStringOps.calcStringAttributesBMP(this, arrayA, a.offset() + (fromIndex << 1), length);
11841187
} else {
11851188
assert a.stride() == 2;
11861189
codeRange = TStringOps.calcStringAttributesUTF32(this, arrayA, a.offset() + fromByteIndex, length);
11871190
}
11881191
sb.updateCodeRange(codeRange);
1189-
sb.ensureCapacityAndInflate(this, a.length(), Stride.fromCodeRangeUTF32AllowImprecise(codeRangeA), inflateProfile, bufferGrowProfile, errorProfile);
1192+
sb.ensureCapacityAndInflate(this, a.length(), Stride.fromCodeRangeUTF32AllowImprecise(codeRange), inflateProfile, bufferGrowProfile, errorProfile);
11901193
TStringOps.arraycopyWithStride(this,
11911194
arrayA, a.offset(), a.stride(), fromIndex,
11921195
sb.buf, 0, sb.stride, sb.length, length);
@@ -1199,7 +1202,7 @@ static void append(TruffleStringBuilderGeneric sb, AbstractTruffleString a, int
11991202
@Bind("this") Node node,
12001203
@Cached @Shared TruffleString.ToIndexableNode toIndexableNode,
12011204
@Cached TStringInternalNodes.GetCodePointLengthNode getCodePointLengthNode,
1202-
@Cached TStringInternalNodes.GetPreciseCodeRangeNode getPreciseCodeRangeNode,
1205+
@Cached @Exclusive TStringInternalNodes.GetPreciseCodeRangeNode getPreciseCodeRangeNode,
12031206
@Cached TStringInternalNodes.CalcStringAttributesNode calcAttributesNode,
12041207
@Cached InlinedConditionProfile calcAttrsProfile,
12051208
@Cached @Shared InlinedBranchProfile bufferGrowProfile,

0 commit comments

Comments
 (0)