37
37
38
38
import com .oracle .truffle .api .utilities .ConditionProfile ;
39
39
import org .jcodings .Encoding ;
40
+ import org .jcodings .exception .EncodingException ;
40
41
import org .jcodings .specific .ASCIIEncoding ;
42
+ import org .jcodings .specific .USASCIIEncoding ;
41
43
import org .joni .Matcher ;
42
44
import org .joni .Option ;
43
45
import org .jruby .Ruby ;
@@ -321,22 +323,41 @@ public ConcatNode(ConcatNode prev) {
321
323
322
324
@ Specialization
323
325
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 );
326
333
}
327
334
328
335
@ Specialization
329
336
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 ());
332
356
}
333
357
334
358
@ TruffleBoundary
335
359
@ Specialization
336
360
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
-
340
361
final int codeRange = other .getCodeRange ();
341
362
final int [] ptr_cr_ret = { codeRange };
342
363
@@ -356,11 +377,50 @@ public RubyString concat(RubyString string, RubyString other) {
356
377
return string ;
357
378
}
358
379
359
- @ Specialization (guards = {"!isInteger(other)" , "!isLong(other)" , "!isRubyString(other)" })
380
+ @ Specialization (guards = {"!isInteger(other)" , "!isLong(other)" , "!isRubyBignum(other)" , "! isRubyString(other)" })
360
381
public Object concat (VirtualFrame frame , RubyString string , Object other ) {
361
382
notDesignedForCompilation ();
362
383
return ruby (frame , "concat StringValue(other)" , "other" , other );
363
384
}
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
+ }
364
424
}
365
425
366
426
@ CoreMethod (names = "%" , required = 1 , argumentsAsArray = true )
0 commit comments