Skip to content

Commit 1b500a1

Browse files
committed
[Truffle] Filled out more of String#{<<, concat}.
1 parent 3456198 commit 1b500a1

File tree

3 files changed

+68
-14
lines changed

3 files changed

+68
-14
lines changed
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
11
fails:String#<< with Integer concatencates the argument interpreted as a codepoint
2-
fails:String#<< with Integer returns a ASCII-8BIT string if self is US-ASCII and the argument is between 128-255 (inclusive)
3-
fails:String#<< with Integer raises RangeError if the argument is an invalid codepoint for self's encoding
4-
fails:String#<< with Integer raises RangeError if the argument is negative
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
11
fails:String#concat with Integer concatencates the argument interpreted as a codepoint
2-
fails:String#concat with Integer returns a ASCII-8BIT string if self is US-ASCII and the argument is between 128-255 (inclusive)
3-
fails:String#concat with Integer raises RangeError if the argument is an invalid codepoint for self's encoding
4-
fails:String#concat with Integer raises RangeError if the argument is negative

truffle/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737

3838
import com.oracle.truffle.api.utilities.ConditionProfile;
3939
import org.jcodings.Encoding;
40+
import org.jcodings.exception.EncodingException;
4041
import org.jcodings.specific.ASCIIEncoding;
42+
import org.jcodings.specific.USASCIIEncoding;
4143
import org.joni.Matcher;
4244
import org.joni.Option;
4345
import org.jruby.Ruby;
@@ -321,22 +323,41 @@ public ConcatNode(ConcatNode prev) {
321323

322324
@Specialization
323325
public RubyString concat(RubyString string, int other) {
324-
string.getByteList().append((byte) other);
325-
return string;
326+
if (other < 0) {
327+
CompilerDirectives.transferToInterpreter();
328+
329+
throw new RaiseException(charRangeException(other));
330+
}
331+
332+
return concatNumeric(string, other);
326333
}
327334

328335
@Specialization
329336
public RubyString concat(RubyString string, long other) {
330-
string.getByteList().append((byte) other);
331-
return string;
337+
if (other < 0) {
338+
CompilerDirectives.transferToInterpreter();
339+
340+
throw new RaiseException(charRangeException(other));
341+
}
342+
343+
return concatNumeric(string, (int) other);
344+
}
345+
346+
@Specialization
347+
public RubyString concat(RubyString string, RubyBignum other) {
348+
if (other.bigIntegerValue().signum() < 0) {
349+
CompilerDirectives.transferToInterpreter();
350+
351+
throw new RaiseException(
352+
getContext().getCoreLibrary().rangeError("bignum out of char range", this));
353+
}
354+
355+
return concatNumeric(string, other.bigIntegerValue().intValue());
332356
}
333357

334358
@TruffleBoundary
335359
@Specialization
336360
public RubyString concat(RubyString string, RubyString other) {
337-
// TODO (nirvdrum 06-Feb-15) This shouldn't be designed for compilation because we don't support all the String semantics yet, but a bench9000 benchmark has it on a hot path, so commenting out for now.
338-
//notDesignedForCompilation();
339-
340361
final int codeRange = other.getCodeRange();
341362
final int[] ptr_cr_ret = { codeRange };
342363

@@ -356,11 +377,50 @@ public RubyString concat(RubyString string, RubyString other) {
356377
return string;
357378
}
358379

359-
@Specialization(guards = {"!isInteger(other)", "!isLong(other)", "!isRubyString(other)"})
380+
@Specialization(guards = {"!isInteger(other)", "!isLong(other)", "!isRubyBignum(other)", "!isRubyString(other)"})
360381
public Object concat(VirtualFrame frame, RubyString string, Object other) {
361382
notDesignedForCompilation();
362383
return ruby(frame, "concat StringValue(other)", "other", other);
363384
}
385+
386+
@TruffleBoundary
387+
private RubyString concatNumeric(RubyString string, int c) {
388+
// Taken from org.jruby.RubyString#concatNumeric
389+
390+
final ByteList value = string.getByteList();
391+
Encoding enc = value.getEncoding();
392+
int cl;
393+
394+
try {
395+
cl = StringSupport.codeLength(getContext().getRuntime(), enc, c);
396+
string.modify(value.getRealSize() + cl);
397+
string.clearCodeRange();
398+
399+
if (enc == USASCIIEncoding.INSTANCE) {
400+
if (c > 0xff) {
401+
throw new RaiseException(charRangeException(c));
402+
403+
}
404+
if (c > 0x79) {
405+
value.setEncoding(ASCIIEncoding.INSTANCE);
406+
enc = value.getEncoding();
407+
}
408+
}
409+
410+
enc.codeToMbc(c, value.getUnsafeBytes(), value.getBegin() + value.getRealSize());
411+
} catch (EncodingException e) {
412+
throw new RaiseException(charRangeException(c));
413+
}
414+
415+
value.setRealSize(value.getRealSize() + cl);
416+
417+
return string;
418+
}
419+
420+
private RubyException charRangeException(Number value) {
421+
return getContext().getCoreLibrary().rangeError(
422+
String.format("%d out of char range", value), this);
423+
}
364424
}
365425

366426
@CoreMethod(names = "%", required = 1, argumentsAsArray = true)

0 commit comments

Comments
 (0)