src/jdk/nashorn/internal/objects/NativeRegExp.java

Print this page




 506      * @return last regexp input
 507      */
 508     @Getter(where = Where.CONSTRUCTOR, attributes = Attribute.CONSTANT, name = "$8")
 509     public static Object getGroup8(Object self) {
 510         final RegExpResult match = Global.instance().getLastRegExpResult();
 511         return match == null ? "" : match.getGroup(8);
 512     }
 513 
 514     /**
 515      * Getter for non-standard RegExp.$9 property.
 516      * @param self self object
 517      * @return last regexp input
 518      */
 519     @Getter(where = Where.CONSTRUCTOR, attributes = Attribute.CONSTANT, name = "$9")
 520     public static Object getGroup9(Object self) {
 521         final RegExpResult match = Global.instance().getLastRegExpResult();
 522         return match == null ? "" : match.getGroup(9);
 523     }
 524 
 525     private RegExpResult execInner(final String string) {

 526         int start = getLastIndex();
 527         if (! regexp.isGlobal()) {
 528             start = 0;
 529         }
 530 
 531         if (start < 0 || start > string.length()) {

 532             setLastIndex(0);

 533             return null;
 534         }
 535 
 536         final RegExpMatcher matcher = regexp.match(string);
 537         if (matcher == null || !matcher.search(start)) {

 538             setLastIndex(0);

 539             return null;
 540         }
 541 
 542         if (regexp.isGlobal()) {
 543             setLastIndex(matcher.end());
 544         }
 545 
 546         final RegExpResult match = new RegExpResult(string, matcher.start(), groups(matcher));
 547         globalObject.setLastRegExpResult(match);
 548         return match;
 549     }
 550 
















 551     /**
 552      * Convert java.util.regex.Matcher groups to JavaScript groups.
 553      * That is, replace null and groups that didn't match with undefined.
 554      */
 555     private Object[] groups(final RegExpMatcher matcher) {
 556         final int groupCount = matcher.groupCount();
 557         final Object[] groups = new Object[groupCount + 1];
 558         final BitVector groupsInNegativeLookahead  = regexp.getGroupsInNegativeLookahead();
 559 
 560         for (int i = 0, lastGroupStart = matcher.start(); i <= groupCount; i++) {
 561             final int groupStart = matcher.start(i);
 562             if (lastGroupStart > groupStart
 563                     || (groupsInNegativeLookahead != null && groupsInNegativeLookahead.isSet(i))) {
 564                 // (1) ECMA 15.10.2.5 NOTE 3: need to clear Atom's captures each time Atom is repeated.
 565                 // (2) ECMA 15.10.2.8 NOTE 3: Backreferences to captures in (?!Disjunction) from elsewhere
 566                 // in the pattern always return undefined because the negative lookahead must fail.
 567                 groups[i] = UNDEFINED;
 568                 continue;
 569             }
 570             final String group = matcher.group(i);


 583      * @return NativeArray of matches, string or null.
 584      */
 585     public Object exec(final String string) {
 586         final RegExpResult match = execInner(string);
 587 
 588         if (match == null) {
 589             return null;
 590         }
 591 
 592         return new NativeRegExpExecResult(match);
 593     }
 594 
 595     /**
 596      * Executes a search for a match within a string based on a regular
 597      * expression.
 598      *
 599      * @param string String to match.
 600      * @return True if a match is found.
 601      */
 602     public Object test(final String string) {
 603         return exec(string) != null;
 604     }
 605 
 606     /**
 607      * Searches and replaces the regular expression portion (match) with the
 608      * replaced text instead. For the "replacement text" parameter, you can use
 609      * the keywords $1 to $2 to replace the original text with values from
 610      * sub-patterns defined within the main pattern.
 611      *
 612      * @param string String to match.
 613      * @param replacement Replacement string.
 614      * @return String with substitutions.
 615      */
 616     Object replace(final String string, final String replacement, final ScriptFunction function) {
 617         final RegExpMatcher matcher = regexp.match(string);
 618 
 619         if (matcher == null) {
 620             return string;
 621         }
 622 
 623         /*


 748         final Object[] groups = groups(matcher);
 749         final Object[] args   = Arrays.copyOf(groups, groups.length + 2);
 750 
 751         args[groups.length]     = matcher.start();
 752         args[groups.length + 1] = string;
 753 
 754         final Object self = function.isStrict() ? UNDEFINED : Global.instance();
 755 
 756         return JSType.toString(ScriptRuntime.apply(function, self, args));
 757     }
 758 
 759     /**
 760      * Breaks up a string into an array of substrings based on a regular
 761      * expression or fixed string.
 762      *
 763      * @param string String to match.
 764      * @param limit  Split limit.
 765      * @return Array of substrings.
 766      */
 767     Object split(final String string, final long limit) {
 768         return split(this, string, limit);
 769     }
 770 
 771     private static Object split(final NativeRegExp regexp0, final String input, final long limit) {
 772         final List<Object> matches = new ArrayList<>();
 773 
 774         final NativeRegExp regexp = new NativeRegExp(regexp0);
 775         regexp.setGlobal(true);
 776 
 777         if (limit == 0L) {
 778             return new NativeArray();
 779         }
 780 


 781         RegExpResult match;
 782         final int inputLength = input.length();
 783         int lastLength = -1;

 784         int lastLastIndex = 0;
 785 
 786         while ((match = regexp.execInner(input)) != null) {
 787             final int lastIndex = match.getIndex() + match.length();
 788 
 789             if (lastIndex > lastLastIndex) {
 790                 matches.add(input.substring(lastLastIndex, match.getIndex()));
 791                 if (match.getGroups().length > 1 && match.getIndex() < inputLength) {
 792                     matches.addAll(Arrays.asList(match.getGroups()).subList(1, match.getGroups().length));



 793                 }
 794 
 795                 lastLength = match.length();
 796                 lastLastIndex = lastIndex;
 797 
 798                 if (matches.size() >= limit) {
 799                     break;
 800                 }
 801             }
 802 
 803             // bump the index to avoid infinite loop
 804             if (regexp.getLastIndex() == match.getIndex()) {
 805                 regexp.setLastIndex(match.getIndex() + 1);


 806             }
 807         }
 808 
 809         if (matches.size() < limit) {
 810             // check special case if we need to append an empty string at the
 811             // end of the match
 812             // if the lastIndex was the entire string
 813             if (lastLastIndex == input.length()) {
 814                 if (lastLength > 0 || regexp.test("") == Boolean.FALSE) {
 815                     matches.add("");
 816                 }
 817             } else {
 818                 matches.add(input.substring(lastLastIndex, inputLength));
 819             }
 820         }
 821 
 822         return new NativeArray(matches.toArray());
 823     }
 824 
 825     /**
 826      * Tests for a match in a string. It returns the index of the match, or -1
 827      * if not found.
 828      *
 829      * @param string String to match.
 830      * @return Index of match.
 831      */
 832     Object search(final String string) {
 833         final RegExpResult match = execInner(string);
 834 
 835         if (match == null) {
 836             return -1;
 837         }
 838 




 506      * @return last regexp input
 507      */
 508     @Getter(where = Where.CONSTRUCTOR, attributes = Attribute.CONSTANT, name = "$8")
 509     public static Object getGroup8(Object self) {
 510         final RegExpResult match = Global.instance().getLastRegExpResult();
 511         return match == null ? "" : match.getGroup(8);
 512     }
 513 
 514     /**
 515      * Getter for non-standard RegExp.$9 property.
 516      * @param self self object
 517      * @return last regexp input
 518      */
 519     @Getter(where = Where.CONSTRUCTOR, attributes = Attribute.CONSTANT, name = "$9")
 520     public static Object getGroup9(Object self) {
 521         final RegExpResult match = Global.instance().getLastRegExpResult();
 522         return match == null ? "" : match.getGroup(9);
 523     }
 524 
 525     private RegExpResult execInner(final String string) {
 526         final boolean isGlobal = regexp.isGlobal();
 527         int start = getLastIndex();
 528         if (!isGlobal) {
 529             start = 0;
 530         }
 531 
 532         if (start < 0 || start > string.length()) {
 533             if (isGlobal) {
 534                 setLastIndex(0);
 535             }
 536             return null;
 537         }
 538 
 539         final RegExpMatcher matcher = regexp.match(string);
 540         if (matcher == null || !matcher.search(start)) {
 541             if (isGlobal) {
 542                 setLastIndex(0);
 543             }
 544             return null;
 545         }
 546 
 547         if (isGlobal) {
 548             setLastIndex(matcher.end());
 549         }
 550 
 551         final RegExpResult match = new RegExpResult(string, matcher.start(), groups(matcher));
 552         globalObject.setLastRegExpResult(match);
 553         return match;
 554     }
 555 
 556     // String.prototype.split method ignores the global flag and should not update lastIndex property.
 557     private RegExpResult execSplit(final String string, int start) {
 558         if (start < 0 || start > string.length()) {
 559             return null;
 560         }
 561 
 562         final RegExpMatcher matcher = regexp.match(string);
 563         if (matcher == null || !matcher.search(start)) {
 564             return null;
 565         }
 566 
 567         final RegExpResult match = new RegExpResult(string, matcher.start(), groups(matcher));
 568         globalObject.setLastRegExpResult(match);
 569         return match;
 570     }
 571 
 572     /**
 573      * Convert java.util.regex.Matcher groups to JavaScript groups.
 574      * That is, replace null and groups that didn't match with undefined.
 575      */
 576     private Object[] groups(final RegExpMatcher matcher) {
 577         final int groupCount = matcher.groupCount();
 578         final Object[] groups = new Object[groupCount + 1];
 579         final BitVector groupsInNegativeLookahead  = regexp.getGroupsInNegativeLookahead();
 580 
 581         for (int i = 0, lastGroupStart = matcher.start(); i <= groupCount; i++) {
 582             final int groupStart = matcher.start(i);
 583             if (lastGroupStart > groupStart
 584                     || (groupsInNegativeLookahead != null && groupsInNegativeLookahead.isSet(i))) {
 585                 // (1) ECMA 15.10.2.5 NOTE 3: need to clear Atom's captures each time Atom is repeated.
 586                 // (2) ECMA 15.10.2.8 NOTE 3: Backreferences to captures in (?!Disjunction) from elsewhere
 587                 // in the pattern always return undefined because the negative lookahead must fail.
 588                 groups[i] = UNDEFINED;
 589                 continue;
 590             }
 591             final String group = matcher.group(i);


 604      * @return NativeArray of matches, string or null.
 605      */
 606     public Object exec(final String string) {
 607         final RegExpResult match = execInner(string);
 608 
 609         if (match == null) {
 610             return null;
 611         }
 612 
 613         return new NativeRegExpExecResult(match);
 614     }
 615 
 616     /**
 617      * Executes a search for a match within a string based on a regular
 618      * expression.
 619      *
 620      * @param string String to match.
 621      * @return True if a match is found.
 622      */
 623     public Object test(final String string) {
 624         return execInner(string) != null;
 625     }
 626 
 627     /**
 628      * Searches and replaces the regular expression portion (match) with the
 629      * replaced text instead. For the "replacement text" parameter, you can use
 630      * the keywords $1 to $2 to replace the original text with values from
 631      * sub-patterns defined within the main pattern.
 632      *
 633      * @param string String to match.
 634      * @param replacement Replacement string.
 635      * @return String with substitutions.
 636      */
 637     Object replace(final String string, final String replacement, final ScriptFunction function) {
 638         final RegExpMatcher matcher = regexp.match(string);
 639 
 640         if (matcher == null) {
 641             return string;
 642         }
 643 
 644         /*


 769         final Object[] groups = groups(matcher);
 770         final Object[] args   = Arrays.copyOf(groups, groups.length + 2);
 771 
 772         args[groups.length]     = matcher.start();
 773         args[groups.length + 1] = string;
 774 
 775         final Object self = function.isStrict() ? UNDEFINED : Global.instance();
 776 
 777         return JSType.toString(ScriptRuntime.apply(function, self, args));
 778     }
 779 
 780     /**
 781      * Breaks up a string into an array of substrings based on a regular
 782      * expression or fixed string.
 783      *
 784      * @param string String to match.
 785      * @param limit  Split limit.
 786      * @return Array of substrings.
 787      */
 788     Object split(final String string, final long limit) {









 789         if (limit == 0L) {
 790             return new NativeArray();
 791         }
 792 
 793         final List<Object> matches = new ArrayList<>();
 794 
 795         RegExpResult match;
 796         final int inputLength = string.length();
 797         int lastLength = -1;
 798         int lastIndex = 0;
 799         int lastLastIndex = 0;
 800 
 801         while ((match = execSplit(string, lastIndex)) != null) {
 802             lastIndex = match.getIndex() + match.length();
 803 
 804             if (lastIndex > lastLastIndex) {
 805                 matches.add(string.substring(lastLastIndex, match.getIndex()));
 806                 final Object[] groups = match.getGroups();
 807                 if (groups.length > 1 && match.getIndex() < inputLength) {
 808                     for (int index = 1; index < groups.length && matches.size() < limit; index++) {
 809                         matches.add(groups[index]);
 810                     }
 811                 }
 812 
 813                 lastLength = match.length();

 814 
 815                 if (matches.size() >= limit) {
 816                     break;
 817                 }
 818             }
 819 
 820             // bump the index to avoid infinite loop
 821             if (lastIndex == lastLastIndex) {
 822                 lastIndex++;
 823             } else {
 824                 lastLastIndex = lastIndex;
 825             }
 826         }
 827 
 828         if (matches.size() < limit) {
 829             // check special case if we need to append an empty string at the
 830             // end of the match
 831             // if the lastIndex was the entire string
 832             if (lastLastIndex == string.length()) {
 833                 if (lastLength > 0 || execSplit("", 0) == null) {
 834                     matches.add("");
 835                 }
 836             } else {
 837                 matches.add(string.substring(lastLastIndex, inputLength));
 838             }
 839         }
 840 
 841         return new NativeArray(matches.toArray());
 842     }
 843 
 844     /**
 845      * Tests for a match in a string. It returns the index of the match, or -1
 846      * if not found.
 847      *
 848      * @param string String to match.
 849      * @return Index of match.
 850      */
 851     Object search(final String string) {
 852         final RegExpResult match = execInner(string);
 853 
 854         if (match == null) {
 855             return -1;
 856         }
 857