Skip to content

Commit 55880ba

Browse files
committed
[GR-32765] Pass an interop array to TruffleRegex if we need to give a substring for startPos like with strscan
* Avoids materializing many substring byte[], which is very inefficient. * Fixes #2407
1 parent 8ede413 commit 55880ba

File tree

2 files changed

+53
-15
lines changed

2 files changed

+53
-15
lines changed

src/main/java/org/truffleruby/core/regexp/TruffleRegexpNodes.java

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ public abstract static class MatchInRegionTRegexNode extends PrimitiveArrayArgum
436436
@Child DispatchNode stringDupNode;
437437
@Child TranslateInteropExceptionNode translateInteropExceptionNode;
438438

439-
@Child RopeNodes.SubstringNode substringNode;
439+
@Child RopeNodes.GetBytesObjectNode getBytesObjectNode;
440440

441441
@Specialization(guards = "libString.isRubyString(string)")
442442
protected Object matchInRegionTRegex(
@@ -466,16 +466,27 @@ protected Object matchInRegionTRegex(
466466
}
467467

468468
int fromIndex = fromPos;
469+
final Object interopByteArray;
470+
final String execMethod;
469471
if (startPosNotZeroProfile.profile(startPos > 0)) {
470-
rope = substring(rope, startPos, toPos - startPos);
472+
// GR-32765: When adopting TruffleString, use a TruffleString substring here instead
471473
// If startPos != 0, then fromPos == startPos.
472474
assert fromPos == startPos;
473475
fromIndex = 0;
476+
477+
if (getBytesObjectNode == null) {
478+
CompilerDirectives.transferToInterpreterAndInvalidate();
479+
getBytesObjectNode = insert(RopeNodes.GetBytesObjectNode.create());
480+
}
481+
interopByteArray = getBytesObjectNode.getRange(rope, startPos, toPos);
482+
execMethod = "exec";
483+
} else {
484+
final byte[] bytes = bytesNode.execute(rope);
485+
interopByteArray = getContext().getEnv().asGuestValue(bytes);
486+
execMethod = "execBytes";
474487
}
475488

476-
final byte[] bytes = bytesNode.execute(rope);
477-
final Object interopByteArray = getContext().getEnv().asGuestValue(bytes);
478-
final Object result = invoke(regexInterop, tRegex, "execBytes", interopByteArray, fromIndex);
489+
final Object result = invoke(regexInterop, tRegex, execMethod, interopByteArray, fromIndex);
479490

480491
final boolean isMatch = (boolean) readMember(resultInterop, result, "isMatch");
481492

@@ -570,15 +581,6 @@ private Object dupString(Object string) {
570581

571582
return stringDupNode.call(string, "dup");
572583
}
573-
574-
private Rope substring(Rope rope, int byteOffset, int byteLength) {
575-
if (substringNode == null) {
576-
CompilerDirectives.transferToInterpreterAndInvalidate();
577-
substringNode = insert(RopeNodes.SubstringNode.create());
578-
}
579-
580-
return substringNode.executeSubstring(rope, byteOffset, byteLength);
581-
}
582584
}
583585

584586
public abstract static class MatchNode extends RubyContextNode {

src/main/java/org/truffleruby/core/rope/Bytes.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,17 @@
1010
package org.truffleruby.core.rope;
1111

1212
import com.oracle.truffle.api.CompilerDirectives.ValueType;
13+
import com.oracle.truffle.api.dsl.Cached;
14+
import com.oracle.truffle.api.interop.InteropLibrary;
15+
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
16+
import com.oracle.truffle.api.interop.TruffleObject;
17+
import com.oracle.truffle.api.library.ExportLibrary;
18+
import com.oracle.truffle.api.library.ExportMessage;
19+
import com.oracle.truffle.api.profiles.BranchProfile;
1320

1421
@ValueType
15-
public final class Bytes {
22+
@ExportLibrary(InteropLibrary.class)
23+
public final class Bytes implements TruffleObject {
1624
public final byte[] array;
1725
public final int offset;
1826
public final int length;
@@ -70,4 +78,32 @@ public Bytes clampedRange(int start, int end) {
7078
public byte get(int i) {
7179
return array[offset + i];
7280
}
81+
82+
// region Array messages for TRegex
83+
@ExportMessage
84+
public boolean hasArrayElements() {
85+
return true;
86+
}
87+
88+
@ExportMessage
89+
public long getArraySize() {
90+
return length;
91+
}
92+
93+
@ExportMessage
94+
public Object readArrayElement(long index,
95+
@Cached BranchProfile errorProfile) throws InvalidArrayIndexException {
96+
if (isArrayElementReadable(index)) {
97+
return get((int) index);
98+
} else {
99+
errorProfile.enter();
100+
throw InvalidArrayIndexException.create(index);
101+
}
102+
}
103+
104+
@ExportMessage
105+
public boolean isArrayElementReadable(long index) {
106+
return index >= 0 && index < length;
107+
}
108+
// endregion
73109
}

0 commit comments

Comments
 (0)