JavascriptRegularExpression.cpp 61 KB

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