es.regexp.constructor.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. var DESCRIPTORS = require('../internals/descriptors');
  2. var global = require('../internals/global');
  3. var uncurryThis = require('../internals/function-uncurry-this');
  4. var isForced = require('../internals/is-forced');
  5. var inheritIfRequired = require('../internals/inherit-if-required');
  6. var createNonEnumerableProperty = require('../internals/create-non-enumerable-property');
  7. var getOwnPropertyNames = require('../internals/object-get-own-property-names').f;
  8. var isPrototypeOf = require('../internals/object-is-prototype-of');
  9. var isRegExp = require('../internals/is-regexp');
  10. var toString = require('../internals/to-string');
  11. var regExpFlags = require('../internals/regexp-flags');
  12. var stickyHelpers = require('../internals/regexp-sticky-helpers');
  13. var proxyAccessor = require('../internals/proxy-accessor');
  14. var redefine = require('../internals/redefine');
  15. var fails = require('../internals/fails');
  16. var hasOwn = require('../internals/has-own-property');
  17. var enforceInternalState = require('../internals/internal-state').enforce;
  18. var setSpecies = require('../internals/set-species');
  19. var wellKnownSymbol = require('../internals/well-known-symbol');
  20. var UNSUPPORTED_DOT_ALL = require('../internals/regexp-unsupported-dot-all');
  21. var UNSUPPORTED_NCG = require('../internals/regexp-unsupported-ncg');
  22. var MATCH = wellKnownSymbol('match');
  23. var NativeRegExp = global.RegExp;
  24. var RegExpPrototype = NativeRegExp.prototype;
  25. var SyntaxError = global.SyntaxError;
  26. var getFlags = uncurryThis(regExpFlags);
  27. var exec = uncurryThis(RegExpPrototype.exec);
  28. var charAt = uncurryThis(''.charAt);
  29. var replace = uncurryThis(''.replace);
  30. var stringIndexOf = uncurryThis(''.indexOf);
  31. var stringSlice = uncurryThis(''.slice);
  32. // TODO: Use only propper RegExpIdentifierName
  33. var IS_NCG = /^\?<[^\s\d!#%&*+<=>@^][^\s!#%&*+<=>@^]*>/;
  34. var re1 = /a/g;
  35. var re2 = /a/g;
  36. // "new" should create a new object, old webkit bug
  37. var CORRECT_NEW = new NativeRegExp(re1) !== re1;
  38. var MISSED_STICKY = stickyHelpers.MISSED_STICKY;
  39. var UNSUPPORTED_Y = stickyHelpers.UNSUPPORTED_Y;
  40. var BASE_FORCED = DESCRIPTORS &&
  41. (!CORRECT_NEW || MISSED_STICKY || UNSUPPORTED_DOT_ALL || UNSUPPORTED_NCG || fails(function () {
  42. re2[MATCH] = false;
  43. // RegExp constructor can alter flags and IsRegExp works correct with @@match
  44. return NativeRegExp(re1) != re1 || NativeRegExp(re2) == re2 || NativeRegExp(re1, 'i') != '/a/i';
  45. }));
  46. var handleDotAll = function (string) {
  47. var length = string.length;
  48. var index = 0;
  49. var result = '';
  50. var brackets = false;
  51. var chr;
  52. for (; index <= length; index++) {
  53. chr = charAt(string, index);
  54. if (chr === '\\') {
  55. result += chr + charAt(string, ++index);
  56. continue;
  57. }
  58. if (!brackets && chr === '.') {
  59. result += '[\\s\\S]';
  60. } else {
  61. if (chr === '[') {
  62. brackets = true;
  63. } else if (chr === ']') {
  64. brackets = false;
  65. } result += chr;
  66. }
  67. } return result;
  68. };
  69. var handleNCG = function (string) {
  70. var length = string.length;
  71. var index = 0;
  72. var result = '';
  73. var named = [];
  74. var names = {};
  75. var brackets = false;
  76. var ncg = false;
  77. var groupid = 0;
  78. var groupname = '';
  79. var chr;
  80. for (; index <= length; index++) {
  81. chr = charAt(string, index);
  82. if (chr === '\\') {
  83. chr = chr + charAt(string, ++index);
  84. } else if (chr === ']') {
  85. brackets = false;
  86. } else if (!brackets) switch (true) {
  87. case chr === '[':
  88. brackets = true;
  89. break;
  90. case chr === '(':
  91. if (exec(IS_NCG, stringSlice(string, index + 1))) {
  92. index += 2;
  93. ncg = true;
  94. }
  95. result += chr;
  96. groupid++;
  97. continue;
  98. case chr === '>' && ncg:
  99. if (groupname === '' || hasOwn(names, groupname)) {
  100. throw new SyntaxError('Invalid capture group name');
  101. }
  102. names[groupname] = true;
  103. named[named.length] = [groupname, groupid];
  104. ncg = false;
  105. groupname = '';
  106. continue;
  107. }
  108. if (ncg) groupname += chr;
  109. else result += chr;
  110. } return [result, named];
  111. };
  112. // `RegExp` constructor
  113. // https://tc39.es/ecma262/#sec-regexp-constructor
  114. if (isForced('RegExp', BASE_FORCED)) {
  115. var RegExpWrapper = function RegExp(pattern, flags) {
  116. var thisIsRegExp = isPrototypeOf(RegExpPrototype, this);
  117. var patternIsRegExp = isRegExp(pattern);
  118. var flagsAreUndefined = flags === undefined;
  119. var groups = [];
  120. var rawPattern = pattern;
  121. var rawFlags, dotAll, sticky, handled, result, state;
  122. if (!thisIsRegExp && patternIsRegExp && flagsAreUndefined && pattern.constructor === RegExpWrapper) {
  123. return pattern;
  124. }
  125. if (patternIsRegExp || isPrototypeOf(RegExpPrototype, pattern)) {
  126. pattern = pattern.source;
  127. if (flagsAreUndefined) flags = 'flags' in rawPattern ? rawPattern.flags : getFlags(rawPattern);
  128. }
  129. pattern = pattern === undefined ? '' : toString(pattern);
  130. flags = flags === undefined ? '' : toString(flags);
  131. rawPattern = pattern;
  132. if (UNSUPPORTED_DOT_ALL && 'dotAll' in re1) {
  133. dotAll = !!flags && stringIndexOf(flags, 's') > -1;
  134. if (dotAll) flags = replace(flags, /s/g, '');
  135. }
  136. rawFlags = flags;
  137. if (MISSED_STICKY && 'sticky' in re1) {
  138. sticky = !!flags && stringIndexOf(flags, 'y') > -1;
  139. if (sticky && UNSUPPORTED_Y) flags = replace(flags, /y/g, '');
  140. }
  141. if (UNSUPPORTED_NCG) {
  142. handled = handleNCG(pattern);
  143. pattern = handled[0];
  144. groups = handled[1];
  145. }
  146. result = inheritIfRequired(NativeRegExp(pattern, flags), thisIsRegExp ? this : RegExpPrototype, RegExpWrapper);
  147. if (dotAll || sticky || groups.length) {
  148. state = enforceInternalState(result);
  149. if (dotAll) {
  150. state.dotAll = true;
  151. state.raw = RegExpWrapper(handleDotAll(pattern), rawFlags);
  152. }
  153. if (sticky) state.sticky = true;
  154. if (groups.length) state.groups = groups;
  155. }
  156. if (pattern !== rawPattern) try {
  157. // fails in old engines, but we have no alternatives for unsupported regex syntax
  158. createNonEnumerableProperty(result, 'source', rawPattern === '' ? '(?:)' : rawPattern);
  159. } catch (error) { /* empty */ }
  160. return result;
  161. };
  162. for (var keys = getOwnPropertyNames(NativeRegExp), index = 0; keys.length > index;) {
  163. proxyAccessor(RegExpWrapper, NativeRegExp, keys[index++]);
  164. }
  165. RegExpPrototype.constructor = RegExpWrapper;
  166. RegExpWrapper.prototype = RegExpPrototype;
  167. redefine(global, 'RegExp', RegExpWrapper);
  168. }
  169. // https://tc39.es/ecma262/#sec-get-regexp-@@species
  170. setSpecies('RegExp');