JavascriptRegularExpression.cpp 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLibraryPch.h"
  6. // Parser Includes
  7. #include "DebugWriter.h"
  8. #include "RegexPattern.h"
  9. using namespace Js;
  10. JavascriptRegExp::JavascriptRegExp(UnifiedRegex::RegexPattern* pattern, DynamicType* type) :
  11. DynamicObject(type),
  12. pattern(pattern),
  13. splitPattern(nullptr),
  14. lastIndexVar(nullptr),
  15. lastIndexOrFlag(0)
  16. {
  17. Assert(type->GetTypeId() == TypeIds_RegEx);
  18. Assert(!this->GetType()->AreThisAndPrototypesEnsuredToHaveOnlyWritableDataProperties());
  19. Assert(!this->GetType()->ThisAndPrototypesHaveNoSpecialProperties());
  20. #if ENABLE_REGEX_CONFIG_OPTIONS
  21. if (REGEX_CONFIG_FLAG(RegexTracing))
  22. {
  23. UnifiedRegex::DebugWriter* w = type->GetScriptContext()->GetRegexDebugWriter();
  24. if (pattern == 0)
  25. w->PrintEOL(_u("// REGEX CREATE"));
  26. else
  27. {
  28. w->Print(_u("// REGEX CREATE "));
  29. pattern->Print(w);
  30. w->EOL();
  31. }
  32. }
  33. #endif
  34. }
  35. JavascriptRegExp::JavascriptRegExp(DynamicType * type) :
  36. DynamicObject(type),
  37. pattern(nullptr),
  38. splitPattern(nullptr),
  39. lastIndexVar(nullptr),
  40. lastIndexOrFlag(0)
  41. {
  42. Assert(type->GetTypeId() == TypeIds_RegEx);
  43. #if DBG
  44. if (REGEX_CONFIG_FLAG(RegexTracing))
  45. {
  46. UnifiedRegex::DebugWriter* w = type->GetScriptContext()->GetRegexDebugWriter();
  47. w->PrintEOL(_u("REGEX CREATE"));
  48. }
  49. #endif
  50. }
  51. JavascriptRegExp::JavascriptRegExp(JavascriptRegExp * instance, bool deepCopy) :
  52. DynamicObject(instance, deepCopy),
  53. pattern(instance->GetPattern()),
  54. splitPattern(instance->GetSplitPattern()),
  55. lastIndexVar(instance->lastIndexVar),
  56. lastIndexOrFlag(instance->lastIndexOrFlag)
  57. {
  58. // For boxing stack instance
  59. Assert(ThreadContext::IsOnStack(instance));
  60. // These members should never be on the stack and thus never need to be deep copied
  61. Assert(!ThreadContext::IsOnStack(instance->GetPattern()));
  62. Assert(!ThreadContext::IsOnStack(instance->GetSplitPattern()));
  63. Assert(!ThreadContext::IsOnStack(instance->lastIndexVar));
  64. }
  65. // IsRegExp in the spec.
  66. bool JavascriptRegExp::IsRegExpLike(Var aValue, ScriptContext* scriptContext)
  67. {
  68. if (scriptContext->GetConfig()->IsES6RegExSymbolsEnabled())
  69. {
  70. if (!JavascriptOperators::IsObject(aValue))
  71. {
  72. return false;
  73. }
  74. Var symbolMatchProperty = JavascriptOperators::GetProperty(
  75. VarTo<RecyclableObject>(aValue),
  76. PropertyIds::_symbolMatch,
  77. scriptContext);
  78. if (!JavascriptOperators::IsUndefined(symbolMatchProperty))
  79. {
  80. return JavascriptConversion::ToBool(symbolMatchProperty, scriptContext);
  81. }
  82. }
  83. return VarIs<JavascriptRegExp>(aValue);
  84. }
  85. CharCount JavascriptRegExp::GetLastIndexProperty(RecyclableObject* instance, ScriptContext* scriptContext)
  86. {
  87. int64 lastIndex = JavascriptConversion::ToLength(
  88. JavascriptOperators::GetProperty(instance, PropertyIds::lastIndex, scriptContext),
  89. scriptContext);
  90. return GetIndexOrMax(lastIndex);
  91. }
  92. void JavascriptRegExp::SetLastIndexProperty(Var instance, CharCount lastIndex, ScriptContext* scriptContext)
  93. {
  94. SetLastIndexProperty(
  95. instance,
  96. JavascriptNumber::ToVar(lastIndex, scriptContext),
  97. scriptContext);
  98. }
  99. void JavascriptRegExp::SetLastIndexProperty(Var instance, Var lastIndex, ScriptContext* scriptContext)
  100. {
  101. JavascriptOperators::SetProperty(
  102. instance,
  103. VarTo<RecyclableObject>(instance),
  104. PropertyIds::lastIndex,
  105. lastIndex,
  106. scriptContext,
  107. static_cast<PropertyOperationFlags>(PropertyOperation_ThrowIfNotExtensible | PropertyOperation_ThrowIfNonWritable));
  108. }
  109. bool JavascriptRegExp::GetGlobalProperty(RecyclableObject* instance, ScriptContext* scriptContext)
  110. {
  111. return JavascriptConversion::ToBool(
  112. JavascriptOperators::GetProperty(instance, PropertyIds::global, scriptContext),
  113. scriptContext);
  114. }
  115. bool JavascriptRegExp::GetUnicodeProperty(RecyclableObject* instance, ScriptContext* scriptContext)
  116. {
  117. return JavascriptConversion::ToBool(
  118. JavascriptOperators::GetProperty(instance, PropertyIds::unicode, scriptContext),
  119. scriptContext);
  120. }
  121. CharCount JavascriptRegExp::AddIndex(CharCount base, CharCount offset)
  122. {
  123. return (base + offset < base) // Overflow?
  124. ? MaxCharCount
  125. : base + offset;
  126. }
  127. CharCount JavascriptRegExp::GetIndexOrMax(int64 index)
  128. {
  129. return (index > SIZE_MAX || IsValidCharCount((size_t) index))
  130. ? (CharCount) index
  131. : MaxCharCount;
  132. }
  133. InternalString JavascriptRegExp::GetSource() const
  134. {
  135. return GetPattern()->GetSource();
  136. }
  137. UnifiedRegex::RegexFlags JavascriptRegExp::GetFlags() const
  138. {
  139. return GetPattern()->GetFlags();
  140. }
  141. JavascriptRegExp* JavascriptRegExp::GetJavascriptRegExp(Arguments& args, PCWSTR propertyName, ScriptContext* scriptContext)
  142. {
  143. if (args.Info.Count == 0)
  144. {
  145. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedRegExp, propertyName);
  146. }
  147. return ToRegExp(args[0], propertyName, scriptContext);
  148. }
  149. JavascriptRegExp* JavascriptRegExp::ToRegExp(Var var, PCWSTR varName, ScriptContext* scriptContext)
  150. {
  151. JavascriptRegExp * regExp = JavascriptOperators::TryFromVar<JavascriptRegExp>(var);
  152. if (regExp)
  153. {
  154. return regExp;
  155. }
  156. if (JavascriptOperators::GetTypeId(var) == TypeIds_HostDispatch)
  157. {
  158. TypeId remoteTypeId = TypeIds_Limit;
  159. RecyclableObject* reclObj = UnsafeVarTo<RecyclableObject>(var);
  160. if (reclObj->GetRemoteTypeId(&remoteTypeId) && remoteTypeId == TypeIds_RegEx)
  161. {
  162. return static_cast<JavascriptRegExp *>(reclObj->GetRemoteObject());
  163. }
  164. }
  165. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedRegExp, varName);
  166. }
  167. RecyclableObject* JavascriptRegExp::GetThisObject(Arguments& args, PCWSTR varName, ScriptContext* scriptContext)
  168. {
  169. if (args.Info.Count == 0 || !JavascriptOperators::IsObject(args[0]))
  170. {
  171. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedObject, varName);
  172. }
  173. return VarTo<RecyclableObject>(args[0]);
  174. }
  175. JavascriptString* JavascriptRegExp::GetFirstStringArg(Arguments& args, ScriptContext* scriptContext)
  176. {
  177. if (args.Info.Count == 1)
  178. {
  179. return scriptContext->GetLibrary()->GetUndefinedDisplayString();
  180. }
  181. else
  182. {
  183. JavascriptString *jsString = JavascriptOperators::TryFromVar<JavascriptString>(args[1]);
  184. if (jsString)
  185. {
  186. return jsString;
  187. }
  188. else
  189. {
  190. return JavascriptConversion::ToString(args[1], scriptContext);
  191. }
  192. }
  193. }
  194. bool JavascriptRegExp::ShouldApplyPrototypeWebWorkaround(Arguments& args, ScriptContext* scriptContext)
  195. {
  196. return scriptContext->GetConfig()->IsES6PrototypeChain() && \
  197. args.Info.Count >= 1 && args[0] == scriptContext->GetLibrary()->GetRegExpPrototype();
  198. }
  199. Var JavascriptRegExp::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  200. {
  201. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  202. ARGUMENTS(args, callInfo);
  203. ScriptContext* scriptContext = function->GetScriptContext();
  204. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  205. // SkipDefaultNewObject function flag should have prevented the default object from
  206. // being created, except when call true a host dispatch.
  207. Var newTarget = args.HasNewTarget() ? args.Values[args.Info.Count] : function;
  208. bool isCtorSuperCall = JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
  209. UnifiedRegex::RegexPattern* pattern = nullptr;
  210. UnifiedRegex::RegexPattern* splitPattern = nullptr;
  211. JavascriptRegExp* regex = nullptr;
  212. if (callInfo.Count < 2)
  213. {
  214. pattern = scriptContext->GetLibrary()->GetEmptyRegexPattern();
  215. }
  216. else if (JavascriptRegExp::IsRegExpLike(args[1], scriptContext))
  217. {
  218. // JavascriptRegExp::IsRegExpLike() makes sure that args[1] is an Object.
  219. RecyclableObject* regexLikeObj = VarTo<RecyclableObject>(args[1]);
  220. if (!(callInfo.Flags & CallFlags_New) &&
  221. (callInfo.Count == 2 || JavascriptOperators::IsUndefinedObject(args[2])) &&
  222. newTarget == JavascriptOperators::GetProperty(regexLikeObj, PropertyIds::constructor, scriptContext))
  223. {
  224. // ES5 15.10.3.1 Called as a function: If pattern R is a regexp object and flags is undefined, then return R unchanged.
  225. // As per ES6 21.2.3.1: We should only return pattern when the this argument is not an uninitialized RegExp object.
  226. // If regex is null, we can be sure the this argument is not initialized.
  227. return regexLikeObj;
  228. }
  229. if (VarIs<JavascriptRegExp>(regexLikeObj))
  230. {
  231. JavascriptRegExp* source = VarTo<JavascriptRegExp>(regexLikeObj);
  232. if (callInfo.Count > 2)
  233. {
  234. // As per ES 2015 21.2.3.1: If 1st argument is RegExp and 2nd argument is flag then return regexp with same pattern as 1st
  235. // argument and flags supplied by the 2nd argument.
  236. if (!JavascriptOperators::IsUndefinedObject(args[2]))
  237. {
  238. InternalString str = source->GetSource();
  239. pattern = CreatePattern(JavascriptString::NewCopyBuffer(str.GetBuffer(), str.GetLength(), scriptContext),
  240. args[2], scriptContext);
  241. // "splitPattern" is a version of "pattern" without the sticky flag. If other flags are the same, we can safely
  242. // reuse "splitPattern".
  243. UnifiedRegex::RegexFlags currentSplitFlags =
  244. static_cast<UnifiedRegex::RegexFlags>(source->GetPattern()->GetFlags() & ~UnifiedRegex::StickyRegexFlag);
  245. UnifiedRegex::RegexFlags newSplitFlags =
  246. static_cast<UnifiedRegex::RegexFlags>(pattern->GetFlags() & ~UnifiedRegex::StickyRegexFlag);
  247. if (newSplitFlags == currentSplitFlags)
  248. {
  249. splitPattern = source->GetSplitPattern();
  250. }
  251. }
  252. }
  253. if (!pattern)
  254. {
  255. pattern = source->GetPattern();
  256. splitPattern = source->GetSplitPattern();
  257. }
  258. }
  259. else // RegExp-like
  260. {
  261. Var source = JavascriptOperators::GetProperty(regexLikeObj, PropertyIds::source, scriptContext);
  262. Var flags = args.Info.Count < 3 || JavascriptOperators::IsUndefinedObject(args[2])
  263. ? JavascriptOperators::GetProperty(regexLikeObj, PropertyIds::flags, scriptContext)
  264. : args[2];
  265. pattern = CreatePattern(source, flags, scriptContext);
  266. }
  267. }
  268. else
  269. {
  270. pattern = CreatePattern(args[1], (callInfo.Count > 2) ? args[2] : nullptr, scriptContext);
  271. }
  272. if (regex == nullptr)
  273. {
  274. regex = scriptContext->GetLibrary()->CreateRegExp(nullptr);
  275. }
  276. regex->SetPattern(pattern);
  277. regex->SetSplitPattern(splitPattern);
  278. return isCtorSuperCall ?
  279. JavascriptOperators::OrdinaryCreateFromConstructor(VarTo<RecyclableObject>(newTarget), regex, nullptr, scriptContext) :
  280. regex;
  281. }
  282. UnifiedRegex::RegexPattern* JavascriptRegExp::CreatePattern(Var aValue, Var options, ScriptContext *scriptContext)
  283. {
  284. JavascriptString * strBody;
  285. if (VarIs<JavascriptString>(aValue))
  286. {
  287. strBody = VarTo<JavascriptString>(aValue);
  288. }
  289. else if (JavascriptOperators::GetTypeId(aValue) == TypeIds_Undefined)
  290. {
  291. strBody = scriptContext->GetLibrary()->GetEmptyString();
  292. }
  293. else
  294. {
  295. strBody = JavascriptConversion::ToString(aValue, scriptContext); // must be null terminated!
  296. }
  297. int cBody = strBody->GetLength();
  298. const char16 *szRegex = strBody->GetSz();
  299. int cOpts = 0;
  300. const char16 *szOptions = nullptr;
  301. JavascriptString * strOptions = nullptr;
  302. if (options != nullptr && !JavascriptOperators::IsUndefinedObject(options))
  303. {
  304. if (VarIs<JavascriptString>(options))
  305. {
  306. strOptions = VarTo<JavascriptString>(options);
  307. }
  308. else
  309. {
  310. strOptions = JavascriptConversion::ToString(options, scriptContext);
  311. }
  312. szOptions = strOptions->GetSz(); // must be null terminated!
  313. cOpts = strOptions->GetLength();
  314. }
  315. UnifiedRegex::RegexPattern* pattern = RegexHelper::CompileDynamic(scriptContext, szRegex, cBody, szOptions, cOpts, false);
  316. return pattern;
  317. }
  318. JavascriptRegExp* JavascriptRegExp::CreateRegEx(const char16* pSource, CharCount sourceLen, UnifiedRegex::RegexFlags flags, ScriptContext *scriptContext)
  319. {
  320. UnifiedRegex::RegexPattern* pattern = RegexHelper::CompileDynamic(scriptContext, pSource, sourceLen, flags, false);
  321. return scriptContext->GetLibrary()->CreateRegExp(pattern);
  322. }
  323. JavascriptRegExp* JavascriptRegExp::CreateRegEx(Var aValue, Var options, ScriptContext *scriptContext)
  324. {
  325. JIT_HELPER_REENTRANT_HEADER(Op_CoerseRegex);
  326. // This is called as helper from OpCode::CoerseRegEx. If aValue is regex pattern /a/, CreatePattern converts
  327. // it to pattern "/a/" instead of "a". So if we know that aValue is regex, then just return the same object
  328. if (VarIs<JavascriptRegExp>(aValue))
  329. {
  330. return VarTo<JavascriptRegExp>(aValue);
  331. }
  332. else
  333. {
  334. return CreateRegExNoCoerce(aValue, options, scriptContext);
  335. }
  336. JIT_HELPER_END(Op_CoerseRegex);
  337. }
  338. JavascriptRegExp* JavascriptRegExp::CreateRegExNoCoerce(Var aValue, Var options, ScriptContext *scriptContext)
  339. {
  340. UnifiedRegex::RegexPattern* pattern = CreatePattern(aValue, options, scriptContext);
  341. return scriptContext->GetLibrary()->CreateRegExp(pattern);
  342. }
  343. void JavascriptRegExp::CacheLastIndex()
  344. {
  345. if (lastIndexVar == nullptr)
  346. lastIndexOrFlag = 0;
  347. else
  348. {
  349. // Does ToInteger(lastIndex) yield an integer in [0, MaxCharCount]?
  350. double v = JavascriptConversion::ToInteger(lastIndexVar, GetScriptContext());
  351. if (JavascriptNumber::IsNan(v))
  352. lastIndexOrFlag = 0;
  353. else if (JavascriptNumber::IsPosInf(v) ||
  354. JavascriptNumber::IsNegInf(v) ||
  355. v < 0.0 ||
  356. v > (double)MaxCharCount)
  357. lastIndexOrFlag = InvalidValue;
  358. else
  359. lastIndexOrFlag = (CharCount)v;
  360. }
  361. }
  362. JavascriptString *JavascriptRegExp::ToString(bool sourceOnly)
  363. {
  364. Js::InternalString str = pattern->GetSource();
  365. CompoundString *const builder = CompoundString::NewWithCharCapacity(str.GetLength() + 5, GetLibrary());
  366. if (!sourceOnly)
  367. {
  368. builder->AppendChars(_u('/'));
  369. }
  370. if (pattern->IsLiteral())
  371. {
  372. builder->AppendChars(str.GetBuffer(), str.GetLength());
  373. }
  374. else
  375. {
  376. // Need to ensure that the resulting static regex is functionally equivalent (as written) to 'this' regex. This
  377. // involves the following:
  378. // - Empty regex should result in /(?:)/ rather than //, which is a comment
  379. // - Unescaped '/' needs to be escaped so that it doesn't end the static regex prematurely
  380. // - Line terminators need to be escaped since they're not allowed in a static regex
  381. if (str.GetLength() == 0)
  382. {
  383. builder->AppendChars(_u("(?:)"));
  384. }
  385. else
  386. {
  387. bool escape = false;
  388. for (charcount_t i = 0; i < str.GetLength(); ++i)
  389. {
  390. const char16 c = str.GetBuffer()[i];
  391. if(!escape)
  392. {
  393. switch(c)
  394. {
  395. case _u('/'):
  396. case _u('\n'):
  397. case _u('\r'):
  398. case _u('\x2028'):
  399. case _u('\x2029'):
  400. // Unescaped '/' or line terminator needs to be escaped
  401. break;
  402. case _u('\\'):
  403. // Escape sequence; the next character is escaped and shouldn't be escaped further
  404. escape = true;
  405. Assert(i + 1 < str.GetLength()); // cannot end in a '\'
  406. // '\' is appended on the next iteration as 'escape' is true. This handles the case where we
  407. // have an escaped line terminator (\<lineTerminator>), where \\n has a different meaning and we
  408. // need to use \n instead.
  409. continue;
  410. default:
  411. builder->AppendChars(c);
  412. continue;
  413. }
  414. }
  415. else
  416. {
  417. escape = false;
  418. }
  419. builder->AppendChars(_u('\\'));
  420. switch(c)
  421. {
  422. // Line terminators need to be escaped. \<lineTerminator> is a special case, where \\n doesn't work
  423. // since that means a '\' followed by an 'n'. We need to use \n instead.
  424. case _u('\n'):
  425. builder->AppendChars(_u('n'));
  426. break;
  427. case _u('\r'):
  428. builder->AppendChars(_u('r'));
  429. break;
  430. case _u('\x2028'):
  431. builder->AppendChars(_u("u2028"));
  432. break;
  433. case _u('\x2029'):
  434. builder->AppendChars(_u("u2029"));
  435. break;
  436. default:
  437. builder->AppendChars(c);
  438. }
  439. }
  440. }
  441. }
  442. if (!sourceOnly)
  443. {
  444. builder->AppendChars(_u('/'));
  445. // Cross-browser compatibility - flags are listed in alphabetical order in the spec and by other browsers
  446. // If you change the order of the flags, don't forget to change it in EntryGetterFlags() and GetOptions() too.
  447. if (pattern->IsGlobal())
  448. {
  449. builder->AppendChars(_u('g'));
  450. }
  451. if (pattern->IsIgnoreCase())
  452. {
  453. builder->AppendChars(_u('i'));
  454. }
  455. if (pattern->IsMultiline())
  456. {
  457. builder->AppendChars(_u('m'));
  458. }
  459. if (pattern->IsDotAll())
  460. {
  461. builder->AppendChars(_u('s'));
  462. }
  463. if (pattern->IsUnicode())
  464. {
  465. builder->AppendChars(_u('u'));
  466. }
  467. if (pattern->IsSticky())
  468. {
  469. builder->AppendChars(_u('y'));
  470. }
  471. }
  472. return builder;
  473. }
  474. Var JavascriptRegExp::EntryCompile(RecyclableObject* function, CallInfo callInfo, ...)
  475. {
  476. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  477. ARGUMENTS(args, callInfo);
  478. ScriptContext* scriptContext = function->GetScriptContext();
  479. JavascriptRegExp* thisRegularExpression = GetJavascriptRegExp(args, _u("RegExp.prototype.compile"), scriptContext);
  480. UnifiedRegex::RegexPattern* pattern;
  481. UnifiedRegex::RegexPattern* splitPattern = nullptr;
  482. if (callInfo.Count == 1 )
  483. {
  484. pattern = scriptContext->GetLibrary()->GetEmptyRegexPattern();
  485. }
  486. else if (VarIs<JavascriptRegExp>(args[1]))
  487. {
  488. JavascriptRegExp* source = VarTo<JavascriptRegExp>(args[1]);
  489. //compile with a regular expression
  490. pattern = source->GetPattern();
  491. splitPattern = source->GetSplitPattern();
  492. // second arg must be undefined if a reg expression is passed
  493. if(callInfo.Count > 2 && JavascriptOperators::GetTypeId(args[2]) != TypeIds_Undefined)
  494. {
  495. JavascriptError::ThrowSyntaxError(scriptContext, JSERR_RegExpSyntax);
  496. }
  497. }
  498. else
  499. {
  500. //compile with a string
  501. JavascriptString * strBody;
  502. if (VarIs<JavascriptString>(args[1]))
  503. {
  504. strBody = VarTo<JavascriptString>(args[1]);
  505. }
  506. else if(JavascriptOperators::GetTypeId(args[1]) == TypeIds_Undefined)
  507. {
  508. strBody = scriptContext->GetLibrary()->GetEmptyString();
  509. }
  510. else
  511. {
  512. strBody = JavascriptConversion::ToString(args[1], scriptContext);
  513. }
  514. int cBody = strBody->GetLength();
  515. const char16 *szRegex = strBody->GetSz(); // must be null terminated!
  516. int cOpts = 0;
  517. const char16 *szOptions = nullptr;
  518. JavascriptString * strOptions = nullptr;
  519. if (callInfo.Count > 2 && !JavascriptOperators::IsUndefinedObject(args[2]))
  520. {
  521. if (VarIs<JavascriptString>(args[2]))
  522. {
  523. strOptions = VarTo<JavascriptString>(args[2]);
  524. }
  525. else
  526. {
  527. strOptions = JavascriptConversion::ToString(args[2], scriptContext);
  528. }
  529. szOptions = strOptions->GetSz(); // must be null terminated!
  530. cOpts = strOptions->GetLength();
  531. }
  532. pattern = RegexHelper::CompileDynamic(scriptContext, szRegex, cBody, szOptions, cOpts, false);
  533. }
  534. thisRegularExpression->SetPattern(pattern);
  535. thisRegularExpression->SetSplitPattern(splitPattern);
  536. thisRegularExpression->SetLastIndex(0);
  537. return thisRegularExpression;
  538. }
  539. Var JavascriptRegExp::OP_NewRegEx(UnifiedRegex::RegexPattern* aCompiledRegex, ScriptContext* scriptContext)
  540. {
  541. JavascriptRegExp * pNewInstance =
  542. RecyclerNew(scriptContext->GetRecycler(), JavascriptRegExp, aCompiledRegex,
  543. scriptContext->GetLibrary()->GetRegexType());
  544. return pNewInstance;
  545. }
  546. Var JavascriptRegExp::EntryExec(RecyclableObject* function, CallInfo callInfo, ...)
  547. {
  548. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  549. ARGUMENTS(args, callInfo);
  550. ScriptContext* scriptContext = function->GetScriptContext();
  551. Assert(!(callInfo.Flags & CallFlags_New));
  552. JavascriptRegExp * pRegEx = GetJavascriptRegExp(args, _u("RegExp.prototype.exec"), scriptContext);
  553. JavascriptString * pStr = GetFirstStringArg(args, scriptContext);
  554. return RegexHelper::RegexExec(scriptContext, pRegEx, pStr, RegexHelper::IsResultNotUsed(callInfo.Flags));
  555. }
  556. Var JavascriptRegExp::EntryTest(RecyclableObject* function, CallInfo callInfo, ...)
  557. {
  558. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  559. ARGUMENTS(args, callInfo);
  560. ScriptContext* scriptContext = function->GetScriptContext();
  561. Assert(!(callInfo.Flags & CallFlags_New));
  562. RecyclableObject *thisObj = GetThisObject(args, _u("RegExp.prototype.test"), scriptContext);
  563. JavascriptString* string = GetFirstStringArg(args, scriptContext);
  564. return RegexHelper::RegexTest(scriptContext, thisObj, string);
  565. }
  566. Var JavascriptRegExp::EntryToString(RecyclableObject* function, CallInfo callInfo, ...)
  567. {
  568. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  569. ARGUMENTS(args, callInfo);
  570. ScriptContext* scriptContext = function->GetScriptContext();
  571. Assert(!(callInfo.Flags & CallFlags_New));
  572. Assert(args.Info.Count > 0);
  573. PCWSTR const varName = _u("RegExp.prototype.toString");
  574. const ScriptConfiguration* scriptConfig = scriptContext->GetConfig();
  575. if (scriptConfig->IsES6RegExPrototypePropertiesEnabled())
  576. {
  577. RecyclableObject *thisObj = GetThisObject(args, varName, scriptContext);
  578. JavascriptString* source = JavascriptConversion::ToString(
  579. JavascriptOperators::GetProperty(thisObj, PropertyIds::source, scriptContext),
  580. scriptContext);
  581. JavascriptString* flags = JavascriptConversion::ToString(
  582. JavascriptOperators::GetProperty(thisObj, PropertyIds::flags, scriptContext),
  583. scriptContext);
  584. CharCount length = source->GetLength() + flags->GetLength() + 2; // 2 for the two '/'s
  585. CompoundString *const builder =
  586. CompoundString::NewWithCharCapacity(length, scriptContext->GetLibrary());
  587. builder->Append(_u('/'));
  588. builder->Append(source);
  589. builder->Append(_u('/'));
  590. builder->Append(flags);
  591. return builder;
  592. }
  593. else
  594. {
  595. JavascriptRegExp* obj = GetJavascriptRegExp(args, varName, scriptContext);
  596. bool sourceOnly = false;
  597. return obj->ToString(sourceOnly);
  598. }
  599. }
  600. Var JavascriptRegExp::EntrySymbolMatch(RecyclableObject* function, CallInfo callInfo, ...)
  601. {
  602. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  603. ARGUMENTS(args, callInfo);
  604. Assert(!(callInfo.Flags & CallFlags_New));
  605. ScriptContext* scriptContext = function->GetScriptContext();
  606. CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(ES6, RegexSymbolMatch, scriptContext);
  607. PCWSTR const varName = _u("RegExp.prototype[Symbol.match]");
  608. RecyclableObject *thisObj = GetThisObject(args, varName, scriptContext);
  609. JavascriptString* string = GetFirstStringArg(args, scriptContext);
  610. return RegexHelper::RegexMatch(
  611. scriptContext,
  612. thisObj,
  613. string,
  614. RegexHelper::IsResultNotUsed(callInfo.Flags));
  615. }
  616. bool JavascriptRegExp::HasOriginalRegExType(RecyclableObject* instance)
  617. {
  618. JavascriptLibrary* library = instance->GetLibrary();
  619. if (instance->GetType() != library->GetRegexType())
  620. {
  621. return false;
  622. }
  623. DynamicObject* regexPrototype = library->GetRegExpPrototype();
  624. return JavascriptOperators::GetPrototype(instance) == regexPrototype
  625. && regexPrototype->GetType() == library->GetRegexPrototypeType();
  626. }
  627. bool JavascriptRegExp::HasObservableConstructor(DynamicObject* regexPrototype)
  628. {
  629. JavascriptLibrary* library = regexPrototype->GetLibrary();
  630. return regexPrototype->GetSlot(library->GetRegexConstructorSlotIndex()) != library->GetRegExpConstructor();
  631. }
  632. bool JavascriptRegExp::HasObservableExec(DynamicObject* regexPrototype)
  633. {
  634. JavascriptLibrary* library = regexPrototype->GetLibrary();
  635. return regexPrototype->GetSlot(library->GetRegexExecSlotIndex()) != library->GetRegexExecFunction();
  636. }
  637. bool JavascriptRegExp::HasObservableFlags(DynamicObject* regexPrototype)
  638. {
  639. JavascriptLibrary* library = regexPrototype->GetLibrary();
  640. return regexPrototype->GetScriptContext()->GetConfig()->IsES6RegExPrototypePropertiesEnabled()
  641. && regexPrototype->GetSlot(library->GetRegexFlagsGetterSlotIndex()) != library->GetRegexFlagsGetterFunction();
  642. }
  643. bool JavascriptRegExp::HasObservableGlobalFlag(DynamicObject* regexPrototype)
  644. {
  645. JavascriptLibrary* library = regexPrototype->GetLibrary();
  646. return regexPrototype->GetScriptContext()->GetConfig()->IsES6RegExPrototypePropertiesEnabled()
  647. && regexPrototype->GetSlot(library->GetRegexGlobalGetterSlotIndex()) != library->GetRegexGlobalGetterFunction();
  648. }
  649. bool JavascriptRegExp::HasObservableUnicodeFlag(DynamicObject* regexPrototype)
  650. {
  651. const ScriptConfiguration* scriptConfig = regexPrototype->GetScriptContext()->GetConfig();
  652. JavascriptLibrary* library = regexPrototype->GetLibrary();
  653. return scriptConfig->IsES6UnicodeExtensionsEnabled()
  654. && scriptConfig->IsES6RegExPrototypePropertiesEnabled()
  655. && regexPrototype->GetSlot(library->GetRegexUnicodeGetterSlotIndex()) != library->GetRegexUnicodeGetterFunction();
  656. }
  657. Var JavascriptRegExp::EntrySymbolReplace(RecyclableObject* function, CallInfo callInfo, ...)
  658. {
  659. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  660. ARGUMENTS(args, callInfo);
  661. Assert(!(callInfo.Flags & CallFlags_New));
  662. ScriptContext* scriptContext = function->GetScriptContext();
  663. CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(ES6, RegexSymbolReplace, scriptContext);
  664. PCWSTR varName = _u("RegExp.prototype[Symbol.replace]");
  665. RecyclableObject* thisObj = GetThisObject(args, varName, scriptContext);
  666. JavascriptString* string = GetFirstStringArg(args, scriptContext);
  667. Var replaceValue = (args.Info.Count > 2) ? args[2] : scriptContext->GetLibrary()->GetUndefined();
  668. if (JavascriptConversion::IsCallable(replaceValue))
  669. {
  670. RecyclableObject* replaceFunction = VarTo<RecyclableObject>(replaceValue);
  671. return RegexHelper::RegexReplaceFunction(scriptContext, thisObj, string, replaceFunction);
  672. }
  673. else
  674. {
  675. JavascriptString* replaceString = JavascriptConversion::ToString(replaceValue, scriptContext);
  676. return RegexHelper::RegexReplace(
  677. scriptContext,
  678. thisObj,
  679. string,
  680. replaceString,
  681. RegexHelper::IsResultNotUsed(callInfo.Flags));
  682. }
  683. }
  684. Var JavascriptRegExp::EntrySymbolSearch(RecyclableObject* function, CallInfo callInfo, ...)
  685. {
  686. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  687. ARGUMENTS(args, callInfo);
  688. Assert(!(callInfo.Flags & CallFlags_New));
  689. ScriptContext* scriptContext = function->GetScriptContext();
  690. CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(ES6, RegexSymbolSearch, scriptContext);
  691. PCWSTR const varName = _u("RegExp.prototype[Symbol.search]");
  692. RecyclableObject *thisObj = GetThisObject(args, varName, scriptContext);
  693. Var regEx = args[0];
  694. JavascriptString* string = GetFirstStringArg(args, scriptContext);
  695. Var previousLastIndex = JavascriptOperators::GetProperty(thisObj, PropertyIds::lastIndex, scriptContext);
  696. SetLastIndexProperty(regEx, TaggedInt::ToVarUnchecked(0), scriptContext);
  697. Var result = CallExec(thisObj, string, varName, scriptContext);
  698. SetLastIndexProperty(regEx, previousLastIndex, scriptContext);
  699. return JavascriptOperators::IsNull(result)
  700. ? TaggedInt::ToVarUnchecked(-1)
  701. : JavascriptOperators::GetProperty(VarTo<RecyclableObject>(result), PropertyIds::index, scriptContext);
  702. }
  703. Var JavascriptRegExp::EntrySymbolSplit(RecyclableObject* function, CallInfo callInfo, ...)
  704. {
  705. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  706. ARGUMENTS(args, callInfo);
  707. Assert(!(callInfo.Flags & CallFlags_New));
  708. ScriptContext* scriptContext = function->GetScriptContext();
  709. CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(ES6, RegexSymbolSplit, scriptContext);
  710. RecyclableObject *thisObj = GetThisObject(args, _u("RegExp.prototype[Symbol.match]"), scriptContext);
  711. JavascriptString* string = GetFirstStringArg(args, scriptContext);
  712. // TODO: SPEC DEVIATION
  713. //
  714. // In RegexHelper::RegexSplit, we check if RegExp properties are overridden in order to determine
  715. // if the algorithm is observable. If it is, we go through the new ES6 algorithm, but otherwise, we
  716. // run the faster ES5 version.
  717. //
  718. // According to the spec, we're supposed to process "limit" after we use some of the RegExp properties.
  719. // However, there doesn't seem to be any reason why "limit" processing can't be pulled above the rest
  720. // in the spec. Therefore, we should see if such a spec update is OK. If not, this would have to be
  721. // moved to its correct place in the code.
  722. uint32 limit = (args.Info.Count < 3 || JavascriptOperators::IsUndefinedObject(args[2]))
  723. ? UINT_MAX
  724. : JavascriptConversion::ToUInt32(args[2], scriptContext);
  725. return RegexHelper::RegexSplit(
  726. scriptContext,
  727. thisObj,
  728. string,
  729. limit,
  730. RegexHelper::IsResultNotUsed(callInfo.Flags));
  731. }
  732. Var JavascriptRegExp::CallExec(RecyclableObject* thisObj, JavascriptString* string, PCWSTR varName, ScriptContext* scriptContext)
  733. {
  734. Var exec = JavascriptOperators::GetProperty(thisObj, PropertyIds::exec, scriptContext);
  735. if (JavascriptConversion::IsCallable(exec))
  736. {
  737. RecyclableObject* execFn = UnsafeVarTo<RecyclableObject>(exec);
  738. ThreadContext * threadContext = scriptContext->GetThreadContext();
  739. Var result = threadContext->ExecuteImplicitCall(execFn, ImplicitCall_Accessor, [=]()->Js::Var
  740. {
  741. return CALL_FUNCTION(scriptContext->GetThreadContext(), execFn, CallInfo(CallFlags_Value, 2), thisObj, string);
  742. });
  743. if (!JavascriptOperators::IsObjectOrNull(result))
  744. {
  745. JavascriptError::ThrowTypeError(scriptContext, JSERR_RegExpExecInvalidReturnType, varName);
  746. }
  747. return result;
  748. }
  749. JavascriptRegExp* regExObj = ToRegExp(thisObj, varName, scriptContext);
  750. return RegexHelper::RegexExec(scriptContext, regExObj, string, false);
  751. }
  752. UnifiedRegex::RegexFlags JavascriptRegExp::SetRegexFlag(
  753. PropertyId propertyId,
  754. UnifiedRegex::RegexFlags flags,
  755. UnifiedRegex::RegexFlags flag,
  756. ScriptContext* scriptContext)
  757. {
  758. bool isEnabled = JavascriptConversion::ToBool(
  759. JavascriptOperators::GetProperty(this, propertyId, scriptContext),
  760. scriptContext);
  761. return isEnabled
  762. ? static_cast<UnifiedRegex::RegexFlags>(flags | flag)
  763. : static_cast<UnifiedRegex::RegexFlags>(flags & ~flag);
  764. }
  765. Var JavascriptRegExp::EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...)
  766. {
  767. ARGUMENTS(args, callInfo);
  768. Assert(args.Info.Count > 0);
  769. return args[0];
  770. }
  771. Var JavascriptRegExp::EntryGetterFlags(RecyclableObject* function, CallInfo callInfo, ...)
  772. {
  773. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  774. ARGUMENTS(args, callInfo);
  775. Assert(!(callInfo.Flags & CallFlags_New));
  776. ScriptContext* scriptContext = function->GetScriptContext();
  777. RecyclableObject *thisObj = GetThisObject(args, _u("RegExp.prototype.flags"), scriptContext);
  778. Var flags;
  779. BEGIN_TEMP_ALLOCATOR(tempAlloc, scriptContext, _u("JavascriptRegExp"))
  780. {
  781. StringBuilder<ArenaAllocator> bs(tempAlloc, 5);
  782. #define APPEND_FLAG(propertyId, flag) AppendFlagForFlagsProperty(&bs, thisObj, propertyId, flag, scriptContext)
  783. // If you change the order of the flags, don't forget to change it in GetOptions() and ToString() too.
  784. APPEND_FLAG(PropertyIds::global, _u('g'));
  785. APPEND_FLAG(PropertyIds::ignoreCase, _u('i'));
  786. APPEND_FLAG(PropertyIds::multiline, _u('m'));
  787. ScriptConfiguration const * scriptConfig = scriptContext->GetConfig();
  788. if (scriptConfig->IsES6UnicodeExtensionsEnabled())
  789. {
  790. APPEND_FLAG(PropertyIds::unicode, _u('u'));
  791. }
  792. if (scriptConfig->IsES2018RegExDotAllEnabled())
  793. {
  794. APPEND_FLAG(PropertyIds::dotAll, _u('s'));
  795. }
  796. if (scriptConfig->IsES6RegExStickyEnabled())
  797. {
  798. APPEND_FLAG(PropertyIds::sticky, _u('y'));
  799. }
  800. #undef APPEND_FLAG
  801. flags = Js::JavascriptString::NewCopyBuffer(bs.Detach(), bs.Count(), scriptContext);
  802. }
  803. END_TEMP_ALLOCATOR(tempAlloc, scriptContext);
  804. return flags;
  805. }
  806. void JavascriptRegExp::AppendFlagForFlagsProperty(
  807. StringBuilder<ArenaAllocator>* builder,
  808. RecyclableObject* thisObj,
  809. PropertyId propertyId,
  810. char16 flag,
  811. ScriptContext* scriptContext)
  812. {
  813. Var propertyValue = JavascriptOperators::GetProperty(thisObj, propertyId, scriptContext);
  814. if (JavascriptConversion::ToBoolean(propertyValue, scriptContext))
  815. {
  816. builder->Append(flag);
  817. }
  818. }
  819. Var JavascriptRegExp::EntryGetterOptions(RecyclableObject* function, CallInfo callInfo, ...)
  820. {
  821. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  822. ARGUMENTS(args, callInfo);
  823. Assert(!(callInfo.Flags & CallFlags_New));
  824. ScriptContext* scriptContext = function->GetScriptContext();
  825. if (ShouldApplyPrototypeWebWorkaround(args, scriptContext))
  826. {
  827. return scriptContext->GetLibrary()->GetUndefined();
  828. }
  829. return GetJavascriptRegExp(args, _u("RegExp.prototype.options"), scriptContext)->GetOptions();
  830. }
  831. Var JavascriptRegExp::GetOptions()
  832. {
  833. Var options;
  834. ScriptContext* scriptContext = this->GetLibrary()->GetScriptContext();
  835. BEGIN_TEMP_ALLOCATOR(tempAlloc, scriptContext, _u("JavascriptRegExp"))
  836. {
  837. StringBuilder<ArenaAllocator> bs(tempAlloc, 4);
  838. // If you change the order of the flags, don't forget to change it in EntryGetterFlags() and ToString() too.
  839. if(GetPattern()->IsGlobal())
  840. {
  841. bs.Append(_u('g'));
  842. }
  843. if(GetPattern()->IsIgnoreCase())
  844. {
  845. bs.Append(_u('i'));
  846. }
  847. if(GetPattern()->IsMultiline())
  848. {
  849. bs.Append(_u('m'));
  850. }
  851. if(GetPattern()->IsDotAll())
  852. {
  853. bs.Append(_u('s'));
  854. }
  855. if (GetPattern()->IsUnicode())
  856. {
  857. bs.Append(_u('u'));
  858. }
  859. if (GetPattern()->IsSticky())
  860. {
  861. bs.Append(_u('y'));
  862. }
  863. options = Js::JavascriptString::NewCopyBuffer(bs.Detach(), bs.Count(), scriptContext);
  864. }
  865. END_TEMP_ALLOCATOR(tempAlloc, scriptContext);
  866. return options;
  867. }
  868. Var JavascriptRegExp::EntryGetterSource(RecyclableObject* function, CallInfo callInfo, ...)
  869. {
  870. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  871. ARGUMENTS(args, callInfo);
  872. Assert(!(callInfo.Flags & CallFlags_New));
  873. ScriptContext* scriptContext = function->GetScriptContext();
  874. if (ShouldApplyPrototypeWebWorkaround(args, scriptContext))
  875. {
  876. return JavascriptString::NewCopyBuffer(_u("(?:)"), 4, scriptContext);
  877. }
  878. return GetJavascriptRegExp(args, _u("RegExp.prototype.source"), scriptContext)->ToString(true);
  879. }
  880. #define DEFINE_FLAG_GETTER(methodName, propertyName, patternMethodName) \
  881. Var JavascriptRegExp::##methodName##(RecyclableObject* function, CallInfo callInfo, ...) \
  882. { \
  883. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); \
  884. ARGUMENTS(args, callInfo); \
  885. Assert(!(callInfo.Flags & CallFlags_New)); \
  886. \
  887. ScriptContext* scriptContext = function->GetScriptContext(); \
  888. if (ShouldApplyPrototypeWebWorkaround(args, scriptContext) || args[0] == scriptContext->GetLibrary()->GetRegExpPrototype()) \
  889. {\
  890. return scriptContext->GetLibrary()->GetUndefined(); \
  891. }\
  892. \
  893. JavascriptRegExp* pRegEx = GetJavascriptRegExp(args, _u("RegExp.prototype.") _u(#propertyName), scriptContext); \
  894. return pRegEx->GetLibrary()->CreateBoolean(pRegEx->GetPattern()->##patternMethodName##()); \
  895. }
  896. DEFINE_FLAG_GETTER(EntryGetterGlobal, global, IsGlobal)
  897. DEFINE_FLAG_GETTER(EntryGetterIgnoreCase, ignoreCase, IsIgnoreCase)
  898. DEFINE_FLAG_GETTER(EntryGetterMultiline, multiline, IsMultiline)
  899. DEFINE_FLAG_GETTER(EntryGetterSticky, sticky, IsSticky)
  900. DEFINE_FLAG_GETTER(EntryGetterUnicode, unicode, IsUnicode)
  901. DEFINE_FLAG_GETTER(EntryGetterDotAll, dotAll, IsDotAll)
  902. JavascriptRegExp * JavascriptRegExp::BoxStackInstance(JavascriptRegExp * instance, bool deepCopy)
  903. {
  904. Assert(ThreadContext::IsOnStack(instance));
  905. // On the stack, the we reserved a pointer before the object as to store the boxed value
  906. JavascriptRegExp ** boxedInstanceRef = ((JavascriptRegExp **)instance) - 1;
  907. JavascriptRegExp * boxedInstance = *boxedInstanceRef;
  908. if (boxedInstance)
  909. {
  910. return boxedInstance;
  911. }
  912. Assert(instance->GetTypeHandler()->GetInlineSlotsSize() == 0);
  913. boxedInstance = RecyclerNew(instance->GetRecycler(), JavascriptRegExp, instance, deepCopy);
  914. *boxedInstanceRef = boxedInstance;
  915. return boxedInstance;
  916. }
  917. // Both 'unicode' and 'sticky' special properties could be controlled via config
  918. // flags. Below, 'sticky' is listed after 'unicode' (for no special reason).
  919. // When the 'unicode' config flag is disabled, we want 'unicode' to be excluded
  920. // from the list, but we still want 'sticky' to be included depending on its
  921. // config flag. That's the reason why we have two lists for the special property
  922. // IDs.
  923. #define DEFAULT_SPECIAL_PROPERTY_IDS \
  924. PropertyIds::lastIndex, \
  925. PropertyIds::global, \
  926. PropertyIds::multiline, \
  927. PropertyIds::ignoreCase, \
  928. PropertyIds::source, \
  929. PropertyIds::options
  930. PropertyId const JavascriptRegExp::specialPropertyIdsAll[] =
  931. {
  932. DEFAULT_SPECIAL_PROPERTY_IDS,
  933. PropertyIds::unicode,
  934. PropertyIds::dotAll,
  935. PropertyIds::sticky
  936. };
  937. PropertyId const JavascriptRegExp::specialPropertyIdsWithoutUnicode[] =
  938. {
  939. DEFAULT_SPECIAL_PROPERTY_IDS,
  940. PropertyIds::dotAll,
  941. PropertyIds::sticky
  942. };
  943. PropertyId const JavascriptRegExp::specialPropertyIdsWithoutDotAll[] =
  944. {
  945. DEFAULT_SPECIAL_PROPERTY_IDS,
  946. PropertyIds::unicode,
  947. PropertyIds::sticky
  948. };
  949. PropertyId const JavascriptRegExp::specialPropertyIdsWithoutDotAllOrUnicode[] =
  950. {
  951. DEFAULT_SPECIAL_PROPERTY_IDS,
  952. PropertyIds::sticky
  953. };
  954. PropertyQueryFlags JavascriptRegExp::HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info)
  955. {
  956. const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
  957. #define HAS_PROPERTY(ownProperty) \
  958. return (ownProperty ? PropertyQueryFlags::Property_Found : DynamicObject::HasPropertyQuery(propertyId, info));
  959. switch (propertyId)
  960. {
  961. case PropertyIds::lastIndex:
  962. return PropertyQueryFlags::Property_Found;
  963. case PropertyIds::global:
  964. case PropertyIds::multiline:
  965. case PropertyIds::ignoreCase:
  966. case PropertyIds::source:
  967. case PropertyIds::options:
  968. HAS_PROPERTY(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  969. case PropertyIds::dotAll:
  970. HAS_PROPERTY(scriptConfig->IsES2018RegExDotAllEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled())
  971. case PropertyIds::unicode:
  972. HAS_PROPERTY(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled())
  973. case PropertyIds::sticky:
  974. HAS_PROPERTY(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled())
  975. default:
  976. return DynamicObject::HasPropertyQuery(propertyId, info);
  977. }
  978. #undef HAS_PROPERTY
  979. }
  980. PropertyQueryFlags JavascriptRegExp::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  981. {
  982. return JavascriptRegExp::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
  983. }
  984. PropertyQueryFlags JavascriptRegExp::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  985. {
  986. BOOL result;
  987. if (GetPropertyBuiltIns(propertyId, value, &result))
  988. {
  989. return JavascriptConversion::BooleanToPropertyQueryFlags(result);
  990. }
  991. return DynamicObject::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
  992. }
  993. PropertyQueryFlags JavascriptRegExp::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  994. {
  995. BOOL result;
  996. PropertyRecord const* propertyRecord;
  997. this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
  998. if (propertyRecord != nullptr && GetPropertyBuiltIns(propertyRecord->GetPropertyId(), value, &result))
  999. {
  1000. return JavascriptConversion::BooleanToPropertyQueryFlags(result);
  1001. }
  1002. return DynamicObject::GetPropertyQuery(originalInstance, propertyNameString, value, info, requestContext);
  1003. }
  1004. bool JavascriptRegExp::GetPropertyBuiltIns(PropertyId propertyId, Var* value, BOOL* result)
  1005. {
  1006. const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
  1007. #define GET_FLAG(patternMethod) \
  1008. if (!scriptConfig->IsES6RegExPrototypePropertiesEnabled()) \
  1009. { \
  1010. *value = this->GetLibrary()->CreateBoolean(this->GetPattern()->##patternMethod##()); \
  1011. *result = true; \
  1012. return true; \
  1013. } \
  1014. else \
  1015. { \
  1016. return false; \
  1017. }
  1018. switch (propertyId)
  1019. {
  1020. case PropertyIds::lastIndex:
  1021. if (this->lastIndexVar == nullptr)
  1022. {
  1023. Assert(lastIndexOrFlag <= MaxCharCount);
  1024. this->lastIndexVar = JavascriptNumber::ToVar(lastIndexOrFlag, GetScriptContext());
  1025. }
  1026. *value = this->lastIndexVar;
  1027. *result = true;
  1028. return true;
  1029. case PropertyIds::global:
  1030. GET_FLAG(IsGlobal)
  1031. case PropertyIds::multiline:
  1032. GET_FLAG(IsMultiline)
  1033. case PropertyIds::dotAll:
  1034. GET_FLAG(IsDotAll)
  1035. case PropertyIds::ignoreCase:
  1036. GET_FLAG(IsIgnoreCase)
  1037. case PropertyIds::unicode:
  1038. GET_FLAG(IsUnicode)
  1039. case PropertyIds::sticky:
  1040. GET_FLAG(IsSticky)
  1041. case PropertyIds::source:
  1042. if (!scriptConfig->IsES6RegExPrototypePropertiesEnabled())
  1043. {
  1044. *value = this->ToString(true);
  1045. *result = true;
  1046. return true;
  1047. }
  1048. else
  1049. {
  1050. return false;
  1051. }
  1052. case PropertyIds::options:
  1053. if (!scriptConfig->IsES6RegExPrototypePropertiesEnabled())
  1054. {
  1055. *value = GetOptions();
  1056. *result = true;
  1057. return true;
  1058. }
  1059. else
  1060. {
  1061. return false;
  1062. }
  1063. default:
  1064. return false;
  1065. }
  1066. #undef GET_FLAG
  1067. }
  1068. BOOL JavascriptRegExp::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
  1069. {
  1070. BOOL result;
  1071. if (SetPropertyBuiltIns(propertyId, value, flags, &result))
  1072. {
  1073. return result;
  1074. }
  1075. return DynamicObject::SetProperty(propertyId, value, flags, info);
  1076. }
  1077. BOOL JavascriptRegExp::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
  1078. {
  1079. BOOL result;
  1080. PropertyRecord const * propertyRecord;
  1081. this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
  1082. if (propertyRecord != nullptr && SetPropertyBuiltIns(propertyRecord->GetPropertyId(), value, flags, &result))
  1083. {
  1084. return result;
  1085. }
  1086. return DynamicObject::SetProperty(propertyNameString, value, flags, info);
  1087. }
  1088. bool JavascriptRegExp::SetPropertyBuiltIns(PropertyId propertyId, Var value, PropertyOperationFlags flags, BOOL* result)
  1089. {
  1090. const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
  1091. #define SET_PROPERTY(ownProperty) \
  1092. if (ownProperty) \
  1093. { \
  1094. JavascriptError::ThrowCantAssignIfStrictMode(flags, this->GetScriptContext()); \
  1095. *result = false; \
  1096. return true; \
  1097. } \
  1098. return false;
  1099. switch (propertyId)
  1100. {
  1101. case PropertyIds::lastIndex:
  1102. this->lastIndexVar = value;
  1103. lastIndexOrFlag = NotCachedValue;
  1104. *result = true;
  1105. return true;
  1106. case PropertyIds::global:
  1107. case PropertyIds::multiline:
  1108. case PropertyIds::ignoreCase:
  1109. case PropertyIds::source:
  1110. case PropertyIds::options:
  1111. SET_PROPERTY(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1112. case PropertyIds::dotAll:
  1113. SET_PROPERTY(scriptConfig->IsES2018RegExDotAllEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1114. case PropertyIds::unicode:
  1115. SET_PROPERTY(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1116. case PropertyIds::sticky:
  1117. SET_PROPERTY(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1118. default:
  1119. return false;
  1120. }
  1121. #undef SET_PROPERTY
  1122. }
  1123. BOOL JavascriptRegExp::InitProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
  1124. {
  1125. return SetProperty(propertyId, value, flags, info);
  1126. }
  1127. BOOL JavascriptRegExp::DeleteProperty(PropertyId propertyId, PropertyOperationFlags propertyOperationFlags)
  1128. {
  1129. const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
  1130. #define DELETE_PROPERTY(ownProperty) \
  1131. if (ownProperty) \
  1132. { \
  1133. JavascriptError::ThrowCantDeleteIfStrictMode(propertyOperationFlags, this->GetScriptContext(), this->GetScriptContext()->GetPropertyName(propertyId)->GetBuffer()); \
  1134. return false; \
  1135. } \
  1136. return DynamicObject::DeleteProperty(propertyId, propertyOperationFlags);
  1137. switch (propertyId)
  1138. {
  1139. case PropertyIds::lastIndex:
  1140. DELETE_PROPERTY(true);
  1141. case PropertyIds::global:
  1142. case PropertyIds::multiline:
  1143. case PropertyIds::ignoreCase:
  1144. case PropertyIds::source:
  1145. case PropertyIds::options:
  1146. DELETE_PROPERTY(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1147. case PropertyIds::dotAll:
  1148. DELETE_PROPERTY(scriptConfig->IsES2018RegExDotAllEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1149. case PropertyIds::unicode:
  1150. DELETE_PROPERTY(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1151. case PropertyIds::sticky:
  1152. DELETE_PROPERTY(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1153. default:
  1154. return DynamicObject::DeleteProperty(propertyId, propertyOperationFlags);
  1155. }
  1156. #undef DELETE_PROPERTY
  1157. }
  1158. BOOL JavascriptRegExp::DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags)
  1159. {
  1160. const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
  1161. #define DELETE_PROPERTY(ownProperty) \
  1162. if (ownProperty) \
  1163. { \
  1164. JavascriptError::ThrowCantDeleteIfStrictMode(flags, this->GetScriptContext(), propertyNameString->GetString()); \
  1165. return false; \
  1166. } \
  1167. return DynamicObject::DeleteProperty(propertyNameString, flags);
  1168. if (BuiltInPropertyRecords::lastIndex.Equals(propertyNameString))
  1169. {
  1170. DELETE_PROPERTY(true);
  1171. }
  1172. else if (BuiltInPropertyRecords::global.Equals(propertyNameString)
  1173. || BuiltInPropertyRecords::multiline.Equals(propertyNameString)
  1174. || BuiltInPropertyRecords::ignoreCase.Equals(propertyNameString)
  1175. || BuiltInPropertyRecords::source.Equals(propertyNameString)
  1176. || BuiltInPropertyRecords::options.Equals(propertyNameString))
  1177. {
  1178. DELETE_PROPERTY(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1179. }
  1180. else if (BuiltInPropertyRecords::unicode.Equals(propertyNameString))
  1181. {
  1182. DELETE_PROPERTY(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1183. }
  1184. else if (BuiltInPropertyRecords::dotAll.Equals(propertyNameString))
  1185. {
  1186. DELETE_PROPERTY(scriptConfig->IsES2018RegExDotAllEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1187. }
  1188. else if (BuiltInPropertyRecords::sticky.Equals(propertyNameString))
  1189. {
  1190. DELETE_PROPERTY(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1191. }
  1192. else
  1193. {
  1194. return DynamicObject::DeleteProperty(propertyNameString, flags);
  1195. }
  1196. #undef DELETE_PROPERTY
  1197. }
  1198. DescriptorFlags JavascriptRegExp::GetSetter(PropertyId propertyId, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
  1199. {
  1200. DescriptorFlags result;
  1201. if (GetSetterBuiltIns(propertyId, info, &result))
  1202. {
  1203. return result;
  1204. }
  1205. return DynamicObject::GetSetter(propertyId, setterValue, info, requestContext);
  1206. }
  1207. DescriptorFlags JavascriptRegExp::GetSetter(JavascriptString* propertyNameString, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
  1208. {
  1209. DescriptorFlags result;
  1210. PropertyRecord const * propertyRecord;
  1211. this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
  1212. if (propertyRecord != nullptr && GetSetterBuiltIns(propertyRecord->GetPropertyId(), info, &result))
  1213. {
  1214. return result;
  1215. }
  1216. return DynamicObject::GetSetter(propertyNameString, setterValue, info, requestContext);
  1217. }
  1218. bool JavascriptRegExp::GetSetterBuiltIns(PropertyId propertyId, PropertyValueInfo* info, DescriptorFlags* result)
  1219. {
  1220. const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
  1221. #define GET_SETTER(ownProperty) \
  1222. if (ownProperty) \
  1223. { \
  1224. PropertyValueInfo::SetNoCache(info, this); \
  1225. *result = JavascriptRegExp::IsWritable(propertyId) ? WritableData : Data; \
  1226. return true; \
  1227. } \
  1228. return false;
  1229. switch (propertyId)
  1230. {
  1231. case PropertyIds::lastIndex:
  1232. GET_SETTER(true);
  1233. case PropertyIds::global:
  1234. case PropertyIds::multiline:
  1235. case PropertyIds::ignoreCase:
  1236. case PropertyIds::source:
  1237. case PropertyIds::options:
  1238. GET_SETTER(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1239. case PropertyIds::unicode:
  1240. GET_SETTER(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1241. case PropertyIds::dotAll:
  1242. GET_SETTER(scriptConfig->IsES2018RegExDotAllEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1243. case PropertyIds::sticky:
  1244. GET_SETTER(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1245. default:
  1246. return false;
  1247. }
  1248. #undef GET_SETTER
  1249. }
  1250. BOOL JavascriptRegExp::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  1251. {
  1252. Js::InternalString str = pattern->GetSource();
  1253. stringBuilder->Append(str.GetBuffer(), str.GetLength());
  1254. return TRUE;
  1255. }
  1256. BOOL JavascriptRegExp::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  1257. {
  1258. stringBuilder->AppendCppLiteral(JS_DIAG_TYPE_JavascriptRegExp);
  1259. return TRUE;
  1260. }
  1261. BOOL JavascriptRegExp::IsEnumerable(PropertyId propertyId)
  1262. {
  1263. const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
  1264. #define IS_ENUMERABLE(ownProperty) \
  1265. return (ownProperty ? false : DynamicObject::IsEnumerable(propertyId));
  1266. switch (propertyId)
  1267. {
  1268. case PropertyIds::lastIndex:
  1269. return false;
  1270. case PropertyIds::global:
  1271. case PropertyIds::multiline:
  1272. case PropertyIds::ignoreCase:
  1273. case PropertyIds::source:
  1274. case PropertyIds::options:
  1275. IS_ENUMERABLE(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1276. case PropertyIds::unicode:
  1277. IS_ENUMERABLE(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1278. case PropertyIds::dotAll:
  1279. IS_ENUMERABLE(scriptConfig->IsES2018RegExDotAllEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1280. case PropertyIds::sticky:
  1281. IS_ENUMERABLE(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1282. default:
  1283. return DynamicObject::IsEnumerable(propertyId);
  1284. }
  1285. #undef IS_ENUMERABLE
  1286. }
  1287. BOOL JavascriptRegExp::IsConfigurable(PropertyId propertyId)
  1288. {
  1289. const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
  1290. #define IS_CONFIGURABLE(ownProperty) \
  1291. return (ownProperty ? false : DynamicObject::IsConfigurable(propertyId));
  1292. switch (propertyId)
  1293. {
  1294. case PropertyIds::lastIndex:
  1295. return false;
  1296. case PropertyIds::global:
  1297. case PropertyIds::multiline:
  1298. case PropertyIds::ignoreCase:
  1299. case PropertyIds::source:
  1300. case PropertyIds::options:
  1301. IS_CONFIGURABLE(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1302. case PropertyIds::unicode:
  1303. IS_CONFIGURABLE(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1304. case PropertyIds::dotAll:
  1305. IS_CONFIGURABLE(scriptConfig->IsES2018RegExDotAllEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1306. case PropertyIds::sticky:
  1307. IS_CONFIGURABLE(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1308. default:
  1309. return DynamicObject::IsConfigurable(propertyId);
  1310. }
  1311. #undef IS_CONFIGURABLE
  1312. }
  1313. BOOL JavascriptRegExp::IsWritable(PropertyId propertyId)
  1314. {
  1315. const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
  1316. #define IS_WRITABLE(ownProperty) \
  1317. return (ownProperty ? false : DynamicObject::IsWritable(propertyId));
  1318. switch (propertyId)
  1319. {
  1320. case PropertyIds::lastIndex:
  1321. return true;
  1322. case PropertyIds::global:
  1323. case PropertyIds::multiline:
  1324. case PropertyIds::ignoreCase:
  1325. case PropertyIds::source:
  1326. case PropertyIds::options:
  1327. IS_WRITABLE(!scriptConfig->IsES6RegExPrototypePropertiesEnabled())
  1328. case PropertyIds::dotAll:
  1329. IS_WRITABLE(scriptConfig->IsES2018RegExDotAllEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled())
  1330. case PropertyIds::unicode:
  1331. IS_WRITABLE(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1332. case PropertyIds::sticky:
  1333. IS_WRITABLE(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
  1334. default:
  1335. return DynamicObject::IsWritable(propertyId);
  1336. }
  1337. #undef IS_WRITABLE
  1338. }
  1339. BOOL JavascriptRegExp::GetSpecialPropertyName(uint32 index, JavascriptString ** propertyName, ScriptContext * requestContext)
  1340. {
  1341. uint length = GetSpecialPropertyCount();
  1342. if (index < length)
  1343. {
  1344. *propertyName = requestContext->GetPropertyString(GetSpecialPropertyIdsInlined()[index]);
  1345. return true;
  1346. }
  1347. return false;
  1348. }
  1349. // Returns the number of special non-enumerable properties this type has.
  1350. uint JavascriptRegExp::GetSpecialPropertyCount() const
  1351. {
  1352. if (GetScriptContext()->GetConfig()->IsES6RegExPrototypePropertiesEnabled())
  1353. {
  1354. return 1; // lastIndex
  1355. }
  1356. uint specialPropertyCount = defaultSpecialPropertyIdsCount;
  1357. if (GetScriptContext()->GetConfig()->IsES6UnicodeExtensionsEnabled())
  1358. {
  1359. specialPropertyCount += 1;
  1360. }
  1361. if (GetScriptContext()->GetConfig()->IsES6RegExStickyEnabled())
  1362. {
  1363. specialPropertyCount += 1;
  1364. }
  1365. if (GetScriptContext()->GetConfig()->IsES2018RegExDotAllEnabled())
  1366. {
  1367. specialPropertyCount += 1;
  1368. }
  1369. return specialPropertyCount;
  1370. }
  1371. // Returns the list of special non-enumerable properties for the type.
  1372. PropertyId const * JavascriptRegExp::GetSpecialPropertyIds() const
  1373. {
  1374. return GetSpecialPropertyIdsInlined();
  1375. }
  1376. inline PropertyId const * JavascriptRegExp::GetSpecialPropertyIdsInlined() const
  1377. {
  1378. const ScriptConfiguration* config = GetScriptContext()->GetConfig();
  1379. if (config->IsES6UnicodeExtensionsEnabled())
  1380. {
  1381. if (config->IsES2018RegExDotAllEnabled())
  1382. {
  1383. return specialPropertyIdsAll;
  1384. }
  1385. else
  1386. {
  1387. return specialPropertyIdsWithoutDotAll;
  1388. }
  1389. }
  1390. else
  1391. {
  1392. if (config->IsES2018RegExDotAllEnabled())
  1393. {
  1394. return specialPropertyIdsWithoutUnicode;
  1395. }
  1396. else
  1397. {
  1398. return specialPropertyIdsWithoutDotAllOrUnicode;
  1399. }
  1400. }
  1401. }
  1402. #if ENABLE_TTD
  1403. TTD::NSSnapObjects::SnapObjectType JavascriptRegExp::GetSnapTag_TTD() const
  1404. {
  1405. return TTD::NSSnapObjects::SnapObjectType::SnapRegexObject;
  1406. }
  1407. void JavascriptRegExp::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  1408. {
  1409. TTD::NSSnapObjects::SnapRegexInfo* sri = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapRegexInfo>();
  1410. UnifiedRegex::RegexPattern* pattern = this->pattern;
  1411. alloc.CopyStringIntoWLength(pattern->GetSource().GetBuffer(), pattern->GetSource().GetLength(), sri->RegexStr);
  1412. //split regex should be automatically generated from regex string and flags so no need to exttract it as well
  1413. sri->Flags = this->GetFlags();
  1414. sri->LastIndexVar = TTD_CONVERT_JSVAR_TO_TTDVAR(this->lastIndexVar);
  1415. sri->LastIndexOrFlag = this->lastIndexOrFlag;
  1416. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapRegexInfo*, TTD::NSSnapObjects::SnapObjectType::SnapRegexObject>(objData, sri);
  1417. }
  1418. void JavascriptRegExp::SetLastIndexInfo_TTD(CharCount lastIndex, Js::Var lastVar)
  1419. {
  1420. this->lastIndexOrFlag = lastIndex;
  1421. this->lastIndexVar = lastVar;
  1422. }
  1423. #endif