es.regexp.constructor.js 6.1 KB

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