|
104 | 104 | import com.oracle.truffle.api.dsl.TypeSystemReference;
|
105 | 105 | import com.oracle.truffle.api.profiles.BranchProfile;
|
106 | 106 | import com.oracle.truffle.api.profiles.ConditionProfile;
|
| 107 | +import java.math.BigInteger; |
107 | 108 |
|
108 | 109 | @CoreFunctions(extendClasses = PythonBuiltinClassType.PString)
|
109 | 110 | public final class StringBuiltins extends PythonBuiltins {
|
@@ -436,55 +437,171 @@ public abstract static class RAddNode extends AddNode {
|
436 | 437 | }
|
437 | 438 |
|
438 | 439 | // str.startswith(prefix[, start[, end]])
|
439 |
| - @Builtin(name = "startswith", minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 5) |
| 440 | + @Builtin(name = "startswith", minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 4) |
440 | 441 | @TypeSystemReference(PythonArithmeticTypes.class)
|
441 | 442 | @GenerateNodeFactory
|
442 | 443 | public abstract static class StartsWithNode extends PythonBuiltinNode {
|
443 |
| - @Specialization |
444 |
| - boolean startsWith(String self, String prefix, int start, int end) { |
| 444 | + |
| 445 | + private @Child CastToIndexNode startNode; |
| 446 | + private @Child CastToIndexNode endNode; |
| 447 | + |
| 448 | + private CastToIndexNode getStartNode() { |
| 449 | + if (startNode == null) { |
| 450 | + CompilerDirectives.transferToInterpreterAndInvalidate(); |
| 451 | + startNode = insert(CastToIndexNode.create(TypeError, val -> { |
| 452 | + throw raise(PythonBuiltinClassType.TypeError, "slice indices must be integers or None or have an __index__ method"); |
| 453 | + })); |
| 454 | + } |
| 455 | + return startNode; |
| 456 | + } |
| 457 | + |
| 458 | + private CastToIndexNode getEndNode() { |
| 459 | + if (endNode == null) { |
| 460 | + CompilerDirectives.transferToInterpreterAndInvalidate(); |
| 461 | + endNode = insert(CastToIndexNode.create(TypeError, val -> { |
| 462 | + throw raise(PythonBuiltinClassType.TypeError, "slice indices must be integers or None or have an __index__ method"); |
| 463 | + })); |
| 464 | + } |
| 465 | + return endNode; |
| 466 | + } |
| 467 | + |
| 468 | + @TruffleBoundary |
| 469 | + private static int correctIndex(PInt index, String text) { |
| 470 | + int textLength = text.length(); |
| 471 | + BigInteger bIndex = index.getValue(); |
| 472 | + BigInteger bTextLength = BigInteger.valueOf(textLength); |
| 473 | + if (bIndex.compareTo(BigInteger.ZERO) < 0) { |
| 474 | + BigInteger result = bIndex.add(bTextLength); |
| 475 | + return result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 ? Integer.MIN_VALUE : result.intValue(); |
| 476 | + } |
| 477 | + return bIndex.compareTo(bTextLength) > 0 ? textLength : bIndex.intValue(); |
| 478 | + } |
| 479 | + |
| 480 | + private static int correctIndex(int index, String text) { |
| 481 | + return index < 0 ? index + text.length() : index; |
| 482 | + } |
| 483 | + |
| 484 | + private static int correctIndex(long index, String text) { |
| 485 | + if (index < 0) { |
| 486 | + long result = index + text.length(); |
| 487 | + return result < Integer.MIN_VALUE ? Integer.MIN_VALUE : (int) result; |
| 488 | + } |
| 489 | + return index > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) index; |
| 490 | + } |
| 491 | + |
| 492 | + private static boolean doIt(String text, String prefix, int start, int end) { |
445 | 493 | if (end - start < prefix.length()) {
|
446 | 494 | return false;
|
447 |
| - } else if (self.startsWith(prefix, start)) { |
448 |
| - return true; |
449 | 495 | }
|
450 |
| - return false; |
| 496 | + return text.startsWith(prefix, start < 0 ? 0 : start); |
451 | 497 | }
|
452 | 498 |
|
453 |
| - @Specialization |
454 |
| - boolean startsWith(String self, PTuple prefix, int start, int end) { |
| 499 | + private boolean doIt(String self, PTuple prefix, int start, int end) { |
455 | 500 | for (Object o : prefix.getArray()) {
|
456 | 501 | if (o instanceof String) {
|
457 |
| - if (startsWith(self, (String) o, start, end)) { |
| 502 | + if (doIt(self, (String) o, start, end)) { |
458 | 503 | return true;
|
459 | 504 | }
|
460 | 505 | } else if (o instanceof PString) {
|
461 |
| - if (startsWith(self, ((PString) o).getValue(), start, end)) { |
| 506 | + if (doIt(self, ((PString) o).getValue(), start, end)) { |
462 | 507 | return true;
|
463 | 508 | }
|
| 509 | + } else { |
| 510 | + throw raise(TypeError, "tuple for startswith must only contain str, not %p", o); |
464 | 511 | }
|
465 | 512 | }
|
466 | 513 | return false;
|
467 | 514 | }
|
468 | 515 |
|
| 516 | + @Specialization |
| 517 | + boolean startsWith(String self, String prefix, int start, int end) { |
| 518 | + return doIt(self, prefix, correctIndex(start, self), correctIndex(end, self)); |
| 519 | + } |
| 520 | + |
| 521 | + @Specialization |
| 522 | + boolean startsWith(String self, PTuple prefix, int start, int end) { |
| 523 | + return doIt(self, prefix, correctIndex(start, self), correctIndex(end, self)); |
| 524 | + } |
| 525 | + |
469 | 526 | @Specialization
|
470 | 527 | boolean startsWith(String self, String prefix, int start, @SuppressWarnings("unused") PNone end) {
|
471 |
| - return startsWith(self, prefix, start, self.length()); |
| 528 | + return doIt(self, prefix, correctIndex(start, self), self.length()); |
| 529 | + } |
| 530 | + |
| 531 | + @Specialization |
| 532 | + boolean startsWith(String self, String prefix, long start, @SuppressWarnings("unused") PNone end) { |
| 533 | + return doIt(self, prefix, correctIndex(start, self), self.length()); |
| 534 | + } |
| 535 | + |
| 536 | + @Specialization |
| 537 | + boolean startsWith(String self, String prefix, long start, long end) { |
| 538 | + return doIt(self, prefix, correctIndex(start, self), correctIndex(end, self)); |
| 539 | + } |
| 540 | + |
| 541 | + @Specialization(rewriteOn = ArithmeticException.class) |
| 542 | + boolean startsWith(String self, String prefix, PInt start, @SuppressWarnings("unused") PNone end) { |
| 543 | + return startsWith(self, prefix, start.intValueExact(), self.length()); |
| 544 | + } |
| 545 | + |
| 546 | + @Specialization |
| 547 | + boolean startsWithPIntOvf(String self, String prefix, PInt start, @SuppressWarnings("unused") PNone end) { |
| 548 | + return doIt(self, prefix, correctIndex(start, self), self.length()); |
472 | 549 | }
|
473 | 550 |
|
474 | 551 | @Specialization
|
475 | 552 | boolean startsWith(String self, String prefix, @SuppressWarnings("unused") PNone start, @SuppressWarnings("unused") PNone end) {
|
476 |
| - return startsWith(self, prefix, 0, self.length()); |
| 553 | + return doIt(self, prefix, 0, self.length()); |
477 | 554 | }
|
478 | 555 |
|
479 | 556 | @Specialization
|
480 | 557 | boolean startsWith(String self, PTuple prefix, int start, @SuppressWarnings("unused") PNone end) {
|
481 |
| - return startsWith(self, prefix, start, self.length()); |
| 558 | + return doIt(self, prefix, correctIndex(start, self), self.length()); |
| 559 | + } |
| 560 | + |
| 561 | + @Specialization |
| 562 | + boolean startsWith(String self, PTuple prefix, long start, @SuppressWarnings("unused") PNone end) { |
| 563 | + return doIt(self, prefix, correctIndex(start, self), self.length()); |
| 564 | + } |
| 565 | + |
| 566 | + @Specialization |
| 567 | + boolean startsWith(String self, PTuple prefix, long start, long end) { |
| 568 | + return doIt(self, prefix, correctIndex(start, self), correctIndex(end, self)); |
| 569 | + } |
| 570 | + |
| 571 | + @Specialization(rewriteOn = ArithmeticException.class) |
| 572 | + boolean startsWith(String self, PTuple prefix, PInt start, @SuppressWarnings("unused") PNone end) { |
| 573 | + return startsWith(self, prefix, start.intValueExact(), end); |
| 574 | + } |
| 575 | + |
| 576 | + @Specialization |
| 577 | + boolean startsWithPIntOvf(String self, PTuple prefix, PInt start, @SuppressWarnings("unused") PNone end) { |
| 578 | + return doIt(self, prefix, correctIndex(start, self), self.length()); |
482 | 579 | }
|
483 | 580 |
|
484 | 581 | @Specialization
|
485 | 582 | boolean startsWith(String self, PTuple prefix, @SuppressWarnings("unused") PNone start, @SuppressWarnings("unused") PNone end) {
|
486 | 583 | return startsWith(self, prefix, 0, self.length());
|
487 | 584 | }
|
| 585 | + |
| 586 | + @Specialization |
| 587 | + boolean startsWith(String self, String prefix, Object start, Object end) { |
| 588 | + int sIndex = getStartNode().execute(start); |
| 589 | + int eIndex = getEndNode().execute(end); |
| 590 | + return doIt(self, prefix, correctIndex(sIndex, self), correctIndex(eIndex, self)); |
| 591 | + } |
| 592 | + |
| 593 | + @Specialization |
| 594 | + boolean startsWith(String self, PTuple prefix, Object start, Object end) { |
| 595 | + int sIndex = getStartNode().execute(start); |
| 596 | + int eIndex = getEndNode().execute(end); |
| 597 | + return doIt(self, prefix, correctIndex(sIndex, self), correctIndex(eIndex, self)); |
| 598 | + } |
| 599 | + |
| 600 | + @Fallback |
| 601 | + @SuppressWarnings("unused") |
| 602 | + boolean general(Object self, Object prefix, Object start, Object end) { |
| 603 | + throw raise(TypeError, "startswith first arg must be str or a tuple of str, not %p", prefix); |
| 604 | + } |
488 | 605 | }
|
489 | 606 |
|
490 | 607 | // str.endswith(suffix[, start[, end]])
|
|
0 commit comments