RegExpConstructor.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibJS/Runtime/Error.h>
  7. #include <LibJS/Runtime/GlobalObject.h>
  8. #include <LibJS/Runtime/RegExpConstructor.h>
  9. #include <LibJS/Runtime/RegExpObject.h>
  10. #include <LibJS/Runtime/Value.h>
  11. namespace JS {
  12. JS_DEFINE_ALLOCATOR(RegExpConstructor);
  13. RegExpConstructor::RegExpConstructor(Realm& realm)
  14. : NativeFunction(realm.vm().names.RegExp.as_string(), realm.intrinsics().function_prototype())
  15. {
  16. }
  17. void RegExpConstructor::initialize(Realm& realm)
  18. {
  19. auto& vm = this->vm();
  20. Base::initialize(realm);
  21. // 22.2.5.1 RegExp.prototype, https://tc39.es/ecma262/#sec-regexp.prototype
  22. define_direct_property(vm.names.prototype, realm.intrinsics().regexp_prototype(), 0);
  23. define_native_accessor(realm, vm.well_known_symbol_species(), symbol_species_getter, {}, Attribute::Configurable);
  24. define_direct_property(vm.names.length, Value(2), Attribute::Configurable);
  25. // Additional properties of the RegExp constructor, https://github.com/tc39/proposal-regexp-legacy-features#additional-properties-of-the-regexp-constructor
  26. define_native_accessor(realm, vm.names.input, input_getter, input_setter, Attribute::Configurable);
  27. define_native_accessor(realm, vm.names.inputAlias, input_alias_getter, input_alias_setter, Attribute::Configurable);
  28. define_native_accessor(realm, vm.names.lastMatch, last_match_getter, {}, Attribute::Configurable);
  29. define_native_accessor(realm, vm.names.lastMatchAlias, last_match_alias_getter, {}, Attribute::Configurable);
  30. define_native_accessor(realm, vm.names.lastParen, last_paren_getter, {}, Attribute::Configurable);
  31. define_native_accessor(realm, vm.names.lastParenAlias, last_paren_alias_getter, {}, Attribute::Configurable);
  32. define_native_accessor(realm, vm.names.leftContext, left_context_getter, {}, Attribute::Configurable);
  33. define_native_accessor(realm, vm.names.leftContextAlias, left_context_alias_getter, {}, Attribute::Configurable);
  34. define_native_accessor(realm, vm.names.rightContext, right_context_getter, {}, Attribute::Configurable);
  35. define_native_accessor(realm, vm.names.rightContextAlias, right_context_alias_getter, {}, Attribute::Configurable);
  36. define_native_accessor(realm, vm.names.$1, group_1_getter, {}, Attribute::Configurable);
  37. define_native_accessor(realm, vm.names.$2, group_2_getter, {}, Attribute::Configurable);
  38. define_native_accessor(realm, vm.names.$3, group_3_getter, {}, Attribute::Configurable);
  39. define_native_accessor(realm, vm.names.$4, group_4_getter, {}, Attribute::Configurable);
  40. define_native_accessor(realm, vm.names.$5, group_5_getter, {}, Attribute::Configurable);
  41. define_native_accessor(realm, vm.names.$6, group_6_getter, {}, Attribute::Configurable);
  42. define_native_accessor(realm, vm.names.$7, group_7_getter, {}, Attribute::Configurable);
  43. define_native_accessor(realm, vm.names.$8, group_8_getter, {}, Attribute::Configurable);
  44. define_native_accessor(realm, vm.names.$9, group_9_getter, {}, Attribute::Configurable);
  45. }
  46. // 22.2.4.1 RegExp ( pattern, flags ), https://tc39.es/ecma262/#sec-regexp-pattern-flags
  47. ThrowCompletionOr<Value> RegExpConstructor::call()
  48. {
  49. auto& vm = this->vm();
  50. auto pattern = vm.argument(0);
  51. auto flags = vm.argument(1);
  52. // 1. Let patternIsRegExp be ? IsRegExp(pattern).
  53. bool pattern_is_regexp = TRY(pattern.is_regexp(vm));
  54. // 2. If NewTarget is undefined, then
  55. // a. Let newTarget be the active function object.
  56. auto& new_target = *this;
  57. // b. If patternIsRegExp is true and flags is undefined, then
  58. if (pattern_is_regexp && flags.is_undefined()) {
  59. // i. Let patternConstructor be ? Get(pattern, "constructor").
  60. auto pattern_constructor = TRY(pattern.as_object().get(vm.names.constructor));
  61. // ii. If SameValue(newTarget, patternConstructor) is true, return pattern.
  62. if (same_value(&new_target, pattern_constructor))
  63. return pattern;
  64. }
  65. return TRY(construct(new_target));
  66. }
  67. // 22.2.4.1 RegExp ( pattern, flags ), https://tc39.es/ecma262/#sec-regexp-pattern-flags
  68. ThrowCompletionOr<NonnullGCPtr<Object>> RegExpConstructor::construct(FunctionObject& new_target)
  69. {
  70. auto& vm = this->vm();
  71. auto pattern = vm.argument(0);
  72. auto flags = vm.argument(1);
  73. // 1. Let patternIsRegExp be ? IsRegExp(pattern).
  74. bool pattern_is_regexp = TRY(pattern.is_regexp(vm));
  75. // NOTE: Step 2 is handled in call() above.
  76. // 3. Else, let newTarget be NewTarget.
  77. Value pattern_value;
  78. Value flags_value;
  79. // 4. If pattern is an Object and pattern has a [[RegExpMatcher]] internal slot, then
  80. if (pattern.is_object() && is<RegExpObject>(pattern.as_object())) {
  81. // a. Let P be pattern.[[OriginalSource]].
  82. auto& regexp_pattern = static_cast<RegExpObject&>(pattern.as_object());
  83. pattern_value = PrimitiveString::create(vm, regexp_pattern.pattern());
  84. // b. If flags is undefined, let F be pattern.[[OriginalFlags]].
  85. if (flags.is_undefined())
  86. flags_value = PrimitiveString::create(vm, regexp_pattern.flags());
  87. // c. Else, let F be flags.
  88. else
  89. flags_value = flags;
  90. }
  91. // 5. Else if patternIsRegExp is true, then
  92. else if (pattern_is_regexp) {
  93. // a. Let P be ? Get(pattern, "source").
  94. pattern_value = TRY(pattern.as_object().get(vm.names.source));
  95. // b. If flags is undefined, then
  96. if (flags.is_undefined()) {
  97. // i. Let F be ? Get(pattern, "flags").
  98. flags_value = TRY(pattern.as_object().get(vm.names.flags));
  99. }
  100. // c. Else, let F be flags.
  101. else {
  102. flags_value = flags;
  103. }
  104. }
  105. // 6. Else,
  106. else {
  107. // a. Let P be pattern.
  108. pattern_value = pattern;
  109. // b. Let F be flags.
  110. flags_value = flags;
  111. }
  112. // 7. Let O be ? RegExpAlloc(newTarget).
  113. auto regexp_object = TRY(regexp_alloc(vm, new_target));
  114. // 8. Return ? RegExpInitialize(O, P, F).
  115. return TRY(regexp_object->regexp_initialize(vm, pattern_value, flags_value));
  116. }
  117. // 22.2.5.2 get RegExp [ @@species ], https://tc39.es/ecma262/#sec-get-regexp-@@species
  118. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::symbol_species_getter)
  119. {
  120. // 1. Return the this value.
  121. return vm.this_value();
  122. }
  123. // get RegExp.input, https://github.com/tc39/proposal-regexp-legacy-features#get-regexpinput
  124. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::input_getter)
  125. {
  126. auto regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor();
  127. // 1. Return ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpInput]]).
  128. auto property_getter = &RegExpLegacyStaticProperties::input;
  129. return TRY(get_legacy_regexp_static_property(vm, regexp_constructor, vm.this_value(), property_getter));
  130. }
  131. // get RegExp.$_, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp_
  132. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::input_alias_getter)
  133. {
  134. // Keep the same implementation with `get RegExp.input`
  135. return input_getter(vm);
  136. }
  137. // set RegExp.input, https://github.com/tc39/proposal-regexp-legacy-features#set-regexpinput--val
  138. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::input_setter)
  139. {
  140. auto regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor();
  141. // 1. Perform ? SetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpInput]], val).
  142. auto property_setter = &RegExpLegacyStaticProperties::set_input;
  143. TRY(set_legacy_regexp_static_property(vm, regexp_constructor, vm.this_value(), property_setter, vm.argument(0)));
  144. return js_undefined();
  145. }
  146. // set RegExp.$_, https://github.com/tc39/proposal-regexp-legacy-features#set-regexp_---val
  147. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::input_alias_setter)
  148. {
  149. // Keep the same implementation with `set RegExp.input`
  150. return input_setter(vm);
  151. }
  152. // get RegExp.lastMatch, https://github.com/tc39/proposal-regexp-legacy-features#get-regexplastmatch
  153. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::last_match_getter)
  154. {
  155. auto regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor();
  156. // 1. Return ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpLastMatch]]).
  157. auto property_getter = &RegExpLegacyStaticProperties::last_match;
  158. return TRY(get_legacy_regexp_static_property(vm, regexp_constructor, vm.this_value(), property_getter));
  159. }
  160. // get RegExp.$&, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp
  161. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::last_match_alias_getter)
  162. {
  163. // Keep the same implementation with `get RegExp.lastMatch`
  164. return last_match_getter(vm);
  165. }
  166. // get RegExp.lastParen, https://github.com/tc39/proposal-regexp-legacy-features#get-regexplastparen
  167. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::last_paren_getter)
  168. {
  169. auto regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor();
  170. // 1. Return ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpLastParen]]).
  171. auto property_getter = &RegExpLegacyStaticProperties::last_paren;
  172. return TRY(get_legacy_regexp_static_property(vm, regexp_constructor, vm.this_value(), property_getter));
  173. }
  174. // get RegExp.$+, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp-1
  175. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::last_paren_alias_getter)
  176. {
  177. // Keep the same implementation with `get RegExp.lastParen`
  178. return last_paren_getter(vm);
  179. }
  180. // get RegExp.leftContext, https://github.com/tc39/proposal-regexp-legacy-features#get-regexpleftcontext
  181. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::left_context_getter)
  182. {
  183. auto regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor();
  184. // 1. Return ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpLeftContext]]).
  185. auto property_getter = &RegExpLegacyStaticProperties::left_context;
  186. return TRY(get_legacy_regexp_static_property(vm, regexp_constructor, vm.this_value(), property_getter));
  187. }
  188. // get RegExp.$`, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp-2
  189. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::left_context_alias_getter)
  190. {
  191. // Keep the same implementation with `get RegExp.leftContext`
  192. return left_context_getter(vm);
  193. }
  194. // get RegExp.rightContext, https://github.com/tc39/proposal-regexp-legacy-features#get-regexprightcontext
  195. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::right_context_getter)
  196. {
  197. auto regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor();
  198. // 1. Return ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpRightContext]]).
  199. auto property_getter = &RegExpLegacyStaticProperties::right_context;
  200. return TRY(get_legacy_regexp_static_property(vm, regexp_constructor, vm.this_value(), property_getter));
  201. }
  202. // get RegExp.$', https://github.com/tc39/proposal-regexp-legacy-features#get-regexp-3
  203. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::right_context_alias_getter)
  204. {
  205. // Keep the same implementation with `get RegExp.rightContext`
  206. return right_context_getter(vm);
  207. }
  208. #define DEFINE_REGEXP_GROUP_GETTER(n) \
  209. JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::group_##n##_getter) \
  210. { \
  211. auto regexp_constructor = vm.current_realm()->intrinsics().regexp_constructor(); \
  212. \
  213. /* 1. Return ? GetLegacyRegExpStaticProperty(%RegExp%, this value, [[RegExpParen##n##]]).*/ \
  214. auto property_getter = &RegExpLegacyStaticProperties::$##n; \
  215. return TRY(get_legacy_regexp_static_property(vm, regexp_constructor, vm.this_value(), property_getter)); \
  216. }
  217. // get RegExp.$1, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp1
  218. DEFINE_REGEXP_GROUP_GETTER(1);
  219. // get RegExp.$2, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp2
  220. DEFINE_REGEXP_GROUP_GETTER(2);
  221. // get RegExp.$3, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp3
  222. DEFINE_REGEXP_GROUP_GETTER(3);
  223. // get RegExp.$4, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp4
  224. DEFINE_REGEXP_GROUP_GETTER(4);
  225. // get RegExp.$5, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp5
  226. DEFINE_REGEXP_GROUP_GETTER(5);
  227. // get RegExp.$6, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp6
  228. DEFINE_REGEXP_GROUP_GETTER(6);
  229. // get RegExp.$7, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp7
  230. DEFINE_REGEXP_GROUP_GETTER(7);
  231. // get RegExp.$8, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp8
  232. DEFINE_REGEXP_GROUP_GETTER(8);
  233. // get RegExp.$9, https://github.com/tc39/proposal-regexp-legacy-features#get-regexp9
  234. DEFINE_REGEXP_GROUP_GETTER(9);
  235. #undef DEFINE_REGEXP_GROUP_GETTER
  236. }