| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLibraryPch.h"
- // Parser Includes
- #include "DebugWriter.h"
- #include "RegexPattern.h"
- namespace Js
- {
- JavascriptRegExp::JavascriptRegExp(UnifiedRegex::RegexPattern* pattern, DynamicType* type) :
- DynamicObject(type),
- pattern(pattern),
- splitPattern(nullptr),
- lastIndexVar(nullptr),
- lastIndexOrFlag(0)
- {
- Assert(type->GetTypeId() == TypeIds_RegEx);
- Assert(!this->GetType()->AreThisAndPrototypesEnsuredToHaveOnlyWritableDataProperties());
- #if ENABLE_REGEX_CONFIG_OPTIONS
- if (REGEX_CONFIG_FLAG(RegexTracing))
- {
- UnifiedRegex::DebugWriter* w = type->GetScriptContext()->GetRegexDebugWriter();
- if (pattern == 0)
- w->PrintEOL(_u("// REGEX CREATE"));
- else
- {
- w->Print(_u("// REGEX CREATE "));
- pattern->Print(w);
- w->EOL();
- }
- }
- #endif
- }
- JavascriptRegExp::JavascriptRegExp(DynamicType * type) :
- DynamicObject(type),
- pattern(nullptr),
- splitPattern(nullptr),
- lastIndexVar(nullptr),
- lastIndexOrFlag(0)
- {
- Assert(type->GetTypeId() == TypeIds_RegEx);
- #if DBG
- if (REGEX_CONFIG_FLAG(RegexTracing))
- {
- UnifiedRegex::DebugWriter* w = type->GetScriptContext()->GetRegexDebugWriter();
- w->PrintEOL(_u("REGEX CREATE"));
- }
- #endif
- }
- JavascriptRegExp::JavascriptRegExp(JavascriptRegExp * instance) :
- DynamicObject(instance),
- pattern(instance->GetPattern()),
- splitPattern(instance->GetSplitPattern()),
- lastIndexVar(instance->lastIndexVar),
- lastIndexOrFlag(instance->lastIndexOrFlag)
- {
- // For boxing stack instance
- Assert(ThreadContext::IsOnStack(instance));
- }
- bool JavascriptRegExp::Is(Var aValue)
- {
- return JavascriptOperators::GetTypeId(aValue) == TypeIds_RegEx;
- }
- // IsRegExp in the spec.
- bool JavascriptRegExp::IsRegExpLike(Var aValue, ScriptContext* scriptContext)
- {
- if (scriptContext->GetConfig()->IsES6RegExSymbolsEnabled())
- {
- if (!JavascriptOperators::IsObject(aValue))
- {
- return false;
- }
- Var symbolMatchProperty = JavascriptOperators::GetProperty(
- RecyclableObject::FromVar(aValue),
- PropertyIds::_symbolMatch,
- scriptContext);
- if (!JavascriptOperators::IsUndefined(symbolMatchProperty))
- {
- return JavascriptConversion::ToBool(symbolMatchProperty, scriptContext);
- }
- }
- return JavascriptRegExp::Is(aValue);
- }
- JavascriptRegExp* JavascriptRegExp::FromVar(Var aValue)
- {
- AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptRegExp'");
- return static_cast<JavascriptRegExp *>(RecyclableObject::FromVar(aValue));
- }
- CharCount JavascriptRegExp::GetLastIndexProperty(RecyclableObject* instance, ScriptContext* scriptContext)
- {
- int64 lastIndex = JavascriptConversion::ToLength(
- JavascriptOperators::GetProperty(instance, PropertyIds::lastIndex, scriptContext),
- scriptContext);
- return GetIndexOrMax(lastIndex);
- }
- void JavascriptRegExp::SetLastIndexProperty(Var instance, CharCount lastIndex, ScriptContext* scriptContext)
- {
- SetLastIndexProperty(
- instance,
- JavascriptNumber::ToVar(lastIndex, scriptContext),
- scriptContext);
- }
- void JavascriptRegExp::SetLastIndexProperty(Var instance, Var lastIndex, ScriptContext* scriptContext)
- {
- JavascriptOperators::SetProperty(
- instance,
- RecyclableObject::FromVar(instance),
- PropertyIds::lastIndex,
- lastIndex,
- scriptContext,
- static_cast<PropertyOperationFlags>(PropertyOperation_ThrowIfNotExtensible | PropertyOperation_ThrowIfNonWritable));
- }
- bool JavascriptRegExp::GetGlobalProperty(RecyclableObject* instance, ScriptContext* scriptContext)
- {
- return JavascriptConversion::ToBool(
- JavascriptOperators::GetProperty(instance, PropertyIds::global, scriptContext),
- scriptContext);
- }
- bool JavascriptRegExp::GetUnicodeProperty(RecyclableObject* instance, ScriptContext* scriptContext)
- {
- return JavascriptConversion::ToBool(
- JavascriptOperators::GetProperty(instance, PropertyIds::unicode, scriptContext),
- scriptContext);
- }
- CharCount JavascriptRegExp::AddIndex(CharCount base, CharCount offset)
- {
- return (base + offset < base) // Overflow?
- ? MaxCharCount
- : base + offset;
- }
- CharCount JavascriptRegExp::GetIndexOrMax(int64 index)
- {
- return (index > SIZE_MAX || IsValidCharCount((size_t) index))
- ? (CharCount) index
- : MaxCharCount;
- }
- InternalString JavascriptRegExp::GetSource() const
- {
- return GetPattern()->GetSource();
- }
- UnifiedRegex::RegexFlags JavascriptRegExp::GetFlags() const
- {
- return GetPattern()->GetFlags();
- }
- JavascriptRegExp* JavascriptRegExp::GetJavascriptRegExp(Arguments& args, PCWSTR propertyName, ScriptContext* scriptContext)
- {
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedRegExp, propertyName);
- }
- return ToRegExp(args[0], propertyName, scriptContext);
- }
- JavascriptRegExp* JavascriptRegExp::ToRegExp(Var var, PCWSTR varName, ScriptContext* scriptContext)
- {
- if (JavascriptRegExp::Is(var))
- {
- return JavascriptRegExp::FromVar(var);
- }
- if (JavascriptOperators::GetTypeId(var) == TypeIds_HostDispatch)
- {
- TypeId remoteTypeId = TypeIds_Limit;
- RecyclableObject* reclObj = RecyclableObject::FromVar(var);
- reclObj->GetRemoteTypeId(&remoteTypeId);
- if (remoteTypeId == TypeIds_RegEx)
- {
- return static_cast<JavascriptRegExp *>(reclObj->GetRemoteObject());
- }
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedRegExp, varName);
- }
- RecyclableObject* JavascriptRegExp::GetThisObject(Arguments& args, PCWSTR varName, ScriptContext* scriptContext)
- {
- if (args.Info.Count == 0 || !JavascriptOperators::IsObject(args[0]))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedObject, varName);
- }
- return RecyclableObject::FromVar(args[0]);
- }
- JavascriptString* JavascriptRegExp::GetFirstStringArg(Arguments& args, ScriptContext* scriptContext)
- {
- if (args.Info.Count == 1)
- {
- return scriptContext->GetLibrary()->GetUndefinedDisplayString();
- }
- else if (JavascriptString::Is(args[1]))
- {
- return JavascriptString::FromVar(args[1]);
- }
- else
- {
- return JavascriptConversion::ToString(args[1], scriptContext);
- }
- }
- bool JavascriptRegExp::ShouldApplyPrototypeWebWorkaround(Arguments& args, ScriptContext* scriptContext)
- {
- return scriptContext->GetConfig()->IsES6PrototypeChain() && \
- args.Info.Count >= 1 && args[0] == scriptContext->GetLibrary()->GetRegExpPrototype();
- }
- Var JavascriptRegExp::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- // SkipDefaultNewObject function flag should have prevented the default object from
- // being created, except when call true a host dispatch.
- Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : function;
- bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget);
- Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr
- || JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch);
- UnifiedRegex::RegexPattern* pattern = nullptr;
- UnifiedRegex::RegexPattern* splitPattern = nullptr;
- JavascriptRegExp* regex = nullptr;
- if (callInfo.Count < 2)
- {
- pattern = scriptContext->GetLibrary()->GetEmptyRegexPattern();
- }
- else if (JavascriptRegExp::IsRegExpLike(args[1], scriptContext))
- {
- // JavascriptRegExp::IsRegExpLike() makes sure that args[1] is an Object.
- RecyclableObject* regexLikeObj = RecyclableObject::FromVar(args[1]);
- if (!(callInfo.Flags & CallFlags_New) &&
- (callInfo.Count == 2 || JavascriptOperators::IsUndefinedObject(args[2], scriptContext)) &&
- newTarget == JavascriptOperators::GetProperty(regexLikeObj, PropertyIds::constructor, scriptContext))
- {
- // ES5 15.10.3.1 Called as a function: If pattern R is a regexp object and flags is undefined, then return R unchanged.
- // As per ES6 21.2.3.1: We should only return pattern when the this argument is not an uninitialized RegExp object.
- // If regex is null, we can be sure the this argument is not initialized.
- return regexLikeObj;
- }
- if (JavascriptRegExp::Is(regexLikeObj))
- {
- JavascriptRegExp* source = JavascriptRegExp::FromVar(regexLikeObj);
- if (callInfo.Count > 2)
- {
- // 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
- // argument and flags supplied by the 2nd argument.
- if (!JavascriptOperators::IsUndefinedObject(args[2], scriptContext))
- {
- InternalString str = source->GetSource();
- pattern = CreatePattern(JavascriptString::NewCopyBuffer(str.GetBuffer(), str.GetLength(), scriptContext),
- args[2], scriptContext);
- // "splitPattern" is a version of "pattern" without the sticky flag. If other flags are the same, we can safely
- // reuse "splitPattern".
- UnifiedRegex::RegexFlags currentSplitFlags =
- static_cast<UnifiedRegex::RegexFlags>(source->GetPattern()->GetFlags() & ~UnifiedRegex::StickyRegexFlag);
- UnifiedRegex::RegexFlags newSplitFlags =
- static_cast<UnifiedRegex::RegexFlags>(pattern->GetFlags() & ~UnifiedRegex::StickyRegexFlag);
- if (newSplitFlags == currentSplitFlags)
- {
- splitPattern = source->GetSplitPattern();
- }
- }
- }
- if (!pattern)
- {
- pattern = source->GetPattern();
- splitPattern = source->GetSplitPattern();
- }
- }
- else // RegExp-like
- {
- Var source = JavascriptOperators::GetProperty(regexLikeObj, PropertyIds::source, scriptContext);
- Var flags = args.Info.Count < 3 || JavascriptOperators::IsUndefinedObject(args[2])
- ? JavascriptOperators::GetProperty(regexLikeObj, PropertyIds::flags, scriptContext)
- : args[2];
- pattern = CreatePattern(source, flags, scriptContext);
- }
- }
- else
- {
- pattern = CreatePattern(args[1], (callInfo.Count > 2) ? args[2] : nullptr, scriptContext);
- }
- if (regex == nullptr)
- {
- regex = scriptContext->GetLibrary()->CreateRegExp(nullptr);
- }
- regex->SetPattern(pattern);
- regex->SetSplitPattern(splitPattern);
- return isCtorSuperCall ?
- JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), regex, nullptr, scriptContext) :
- regex;
- }
- UnifiedRegex::RegexPattern* JavascriptRegExp::CreatePattern(Var aValue, Var options, ScriptContext *scriptContext)
- {
- JavascriptString * strBody;
- if (JavascriptString::Is(aValue))
- {
- strBody = JavascriptString::FromVar(aValue);
- }
- else if (JavascriptOperators::GetTypeId(aValue) == TypeIds_Undefined)
- {
- strBody = scriptContext->GetLibrary()->GetEmptyString();
- }
- else
- {
- strBody = JavascriptConversion::ToString(aValue, scriptContext); // must be null terminated!
- }
- int cBody = strBody->GetLength();
- const char16 *szRegex = strBody->GetSz();
- int cOpts = 0;
- const char16 *szOptions = nullptr;
- JavascriptString * strOptions = nullptr;
- if (options != nullptr && !JavascriptOperators::IsUndefinedObject(options, scriptContext))
- {
- if (JavascriptString::Is(options))
- {
- strOptions = JavascriptString::FromVar(options);
- }
- else
- {
- strOptions = JavascriptConversion::ToString(options, scriptContext);
- }
- szOptions = strOptions->GetSz(); // must be null terminated!
- cOpts = strOptions->GetLength();
- }
- UnifiedRegex::RegexPattern* pattern = RegexHelper::CompileDynamic(scriptContext, szRegex, cBody, szOptions, cOpts, false);
- return pattern;
- }
- JavascriptRegExp* JavascriptRegExp::CreateRegEx(const char16* pSource, CharCount sourceLen, UnifiedRegex::RegexFlags flags, ScriptContext *scriptContext)
- {
- UnifiedRegex::RegexPattern* pattern = RegexHelper::CompileDynamic(scriptContext, pSource, sourceLen, flags, false);
- return scriptContext->GetLibrary()->CreateRegExp(pattern);
- }
- JavascriptRegExp* JavascriptRegExp::CreateRegEx(Var aValue, Var options, ScriptContext *scriptContext)
- {
- // This is called as helper from OpCode::CoerseRegEx. If aValue is regex pattern /a/, CreatePattern converts
- // it to pattern "/a/" instead of "a". So if we know that aValue is regex, then just return the same object
- if (JavascriptRegExp::Is(aValue))
- {
- return JavascriptRegExp::FromVar(aValue);
- }
- else
- {
- return CreateRegExNoCoerce(aValue, options, scriptContext);
- }
- }
- JavascriptRegExp* JavascriptRegExp::CreateRegExNoCoerce(Var aValue, Var options, ScriptContext *scriptContext)
- {
- UnifiedRegex::RegexPattern* pattern = CreatePattern(aValue, options, scriptContext);
- return scriptContext->GetLibrary()->CreateRegExp(pattern);
- }
- void JavascriptRegExp::CacheLastIndex()
- {
- if (lastIndexVar == nullptr)
- lastIndexOrFlag = 0;
- else
- {
- // Does ToInteger(lastIndex) yield an integer in [0, MaxCharCount]?
- double v = JavascriptConversion::ToInteger(lastIndexVar, GetScriptContext());
- if (JavascriptNumber::IsNan(v))
- lastIndexOrFlag = 0;
- else if (JavascriptNumber::IsPosInf(v) ||
- JavascriptNumber::IsNegInf(v) ||
- v < 0.0 ||
- v > (double)MaxCharCount)
- lastIndexOrFlag = InvalidValue;
- else
- lastIndexOrFlag = (CharCount)v;
- }
- }
- JavascriptString *JavascriptRegExp::ToString(bool sourceOnly)
- {
- Js::InternalString str = pattern->GetSource();
- CompoundString *const builder = CompoundString::NewWithCharCapacity(str.GetLength() + 5, GetLibrary());
- if (!sourceOnly)
- {
- builder->AppendChars(_u('/'));
- }
- if (pattern->IsLiteral())
- {
- builder->AppendChars(str.GetBuffer(), str.GetLength());
- }
- else
- {
- // Need to ensure that the resulting static regex is functionally equivalent (as written) to 'this' regex. This
- // involves the following:
- // - Empty regex should result in /(?:)/ rather than //, which is a comment
- // - Unescaped '/' needs to be escaped so that it doesn't end the static regex prematurely
- // - Line terminators need to be escaped since they're not allowed in a static regex
- if (str.GetLength() == 0)
- {
- builder->AppendChars(_u("(?:)"));
- }
- else
- {
- bool escape = false;
- for (charcount_t i = 0; i < str.GetLength(); ++i)
- {
- const char16 c = str.GetBuffer()[i];
- if(!escape)
- {
- switch(c)
- {
- case _u('/'):
- case _u('\n'):
- case _u('\r'):
- case _u('\x2028'):
- case _u('\x2029'):
- // Unescaped '/' or line terminator needs to be escaped
- break;
- case _u('\\'):
- // Escape sequence; the next character is escaped and shouldn't be escaped further
- escape = true;
- Assert(i + 1 < str.GetLength()); // cannot end in a '\'
- // '\' is appended on the next iteration as 'escape' is true. This handles the case where we
- // have an escaped line terminator (\<lineTerminator>), where \\n has a different meaning and we
- // need to use \n instead.
- continue;
- default:
- builder->AppendChars(c);
- continue;
- }
- }
- else
- {
- escape = false;
- }
- builder->AppendChars(_u('\\'));
- switch(c)
- {
- // Line terminators need to be escaped. \<lineTerminator> is a special case, where \\n doesn't work
- // since that means a '\' followed by an 'n'. We need to use \n instead.
- case _u('\n'):
- builder->AppendChars(_u('n'));
- break;
- case _u('\r'):
- builder->AppendChars(_u('r'));
- break;
- case _u('\x2028'):
- builder->AppendChars(_u("u2028"));
- break;
- case _u('\x2029'):
- builder->AppendChars(_u("u2029"));
- break;
- default:
- builder->AppendChars(c);
- }
- }
- }
- }
- if (!sourceOnly)
- {
- builder->AppendChars(_u('/'));
- // Cross-browser compatibility - flags are listed in alphabetical order in the spec and by other browsers
- // If you change the order of the flags, don't forget to change it in EntryGetterFlags() and GetOptions() too.
- if (pattern->IsGlobal())
- {
- builder->AppendChars(_u('g'));
- }
- if (pattern->IsIgnoreCase())
- {
- builder->AppendChars(_u('i'));
- }
- if (pattern->IsMultiline())
- {
- builder->AppendChars(_u('m'));
- }
- if (pattern->IsUnicode())
- {
- builder->AppendChars(_u('u'));
- }
- if (pattern->IsSticky())
- {
- builder->AppendChars(_u('y'));
- }
- }
- return builder;
- }
- Var JavascriptRegExp::EntryCompile(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- JavascriptRegExp* thisRegularExpression = GetJavascriptRegExp(args, _u("RegExp.prototype.compile"), scriptContext);
- UnifiedRegex::RegexPattern* pattern;
- UnifiedRegex::RegexPattern* splitPattern = nullptr;
- if (callInfo.Count == 1 )
- {
- pattern = scriptContext->GetLibrary()->GetEmptyRegexPattern();
- }
- else if (JavascriptRegExp::Is(args[1]))
- {
- JavascriptRegExp* source = JavascriptRegExp::FromVar(args[1]);
- //compile with a regular expression
- pattern = source->GetPattern();
- splitPattern = source->GetSplitPattern();
- // second arg must be undefined if a reg expression is passed
- if(callInfo.Count > 2 && JavascriptOperators::GetTypeId(args[2]) != TypeIds_Undefined)
- {
- JavascriptError::ThrowSyntaxError(scriptContext, JSERR_RegExpSyntax);
- }
- }
- else
- {
- //compile with a string
- JavascriptString * strBody;
- if (JavascriptString::Is(args[1]))
- {
- strBody = JavascriptString::FromVar(args[1]);
- }
- else if(JavascriptOperators::GetTypeId(args[1]) == TypeIds_Undefined)
- {
- strBody = scriptContext->GetLibrary()->GetEmptyString();
- }
- else
- {
- strBody = JavascriptConversion::ToString(args[1], scriptContext);
- }
- int cBody = strBody->GetLength();
- const char16 *szRegex = strBody->GetSz(); // must be null terminated!
- int cOpts = 0;
- const char16 *szOptions = nullptr;
- JavascriptString * strOptions = nullptr;
- if (callInfo.Count > 2 && !JavascriptOperators::IsUndefinedObject(args[2], scriptContext))
- {
- if (JavascriptString::Is(args[2]))
- {
- strOptions = JavascriptString::FromVar(args[2]);
- }
- else
- {
- strOptions = JavascriptConversion::ToString(args[2], scriptContext);
- }
- szOptions = strOptions->GetSz(); // must be null terminated!
- cOpts = strOptions->GetLength();
- }
- pattern = RegexHelper::CompileDynamic(scriptContext, szRegex, cBody, szOptions, cOpts, false);
- }
- thisRegularExpression->SetPattern(pattern);
- thisRegularExpression->SetSplitPattern(splitPattern);
- thisRegularExpression->SetLastIndex(0);
- return thisRegularExpression;
- }
- Var JavascriptRegExp::OP_NewRegEx(Var aCompiledRegex, ScriptContext* scriptContext)
- {
- JavascriptRegExp * pNewInstance =
- RecyclerNew(scriptContext->GetRecycler(),JavascriptRegExp,((UnifiedRegex::RegexPattern*)aCompiledRegex),
- scriptContext->GetLibrary()->GetRegexType());
- return pNewInstance;
- }
- Var JavascriptRegExp::EntryExec(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- JavascriptRegExp * pRegEx = GetJavascriptRegExp(args, _u("RegExp.prototype.exec"), scriptContext);
- JavascriptString * pStr = GetFirstStringArg(args, scriptContext);
- return RegexHelper::RegexExec(scriptContext, pRegEx, pStr, RegexHelper::IsResultNotUsed(callInfo.Flags));
- }
- Var JavascriptRegExp::EntryTest(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- RecyclableObject *thisObj = GetThisObject(args, _u("RegExp.prototype.test"), scriptContext);
- JavascriptString* string = GetFirstStringArg(args, scriptContext);
- return RegexHelper::RegexTest(scriptContext, thisObj, string);
- }
- Var JavascriptRegExp::EntryToString(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- Assert(args.Info.Count > 0);
- PCWSTR const varName = _u("RegExp.prototype.toString");
- const ScriptConfiguration* scriptConfig = scriptContext->GetConfig();
- if (scriptConfig->IsES6RegExPrototypePropertiesEnabled())
- {
- RecyclableObject *thisObj = GetThisObject(args, varName, scriptContext);
- JavascriptString* source = JavascriptConversion::ToString(
- JavascriptOperators::GetProperty(thisObj, PropertyIds::source, scriptContext),
- scriptContext);
- JavascriptString* flags = JavascriptConversion::ToString(
- JavascriptOperators::GetProperty(thisObj, PropertyIds::flags, scriptContext),
- scriptContext);
- CharCount length = source->GetLength() + flags->GetLength() + 2; // 2 for the two '/'s
- CompoundString *const builder =
- CompoundString::NewWithCharCapacity(length, scriptContext->GetLibrary());
- builder->Append(_u('/'));
- builder->Append(source);
- builder->Append(_u('/'));
- builder->Append(flags);
- return builder;
- }
- else
- {
- JavascriptRegExp* obj = GetJavascriptRegExp(args, varName, scriptContext);
- bool sourceOnly = false;
- return obj->ToString(sourceOnly);
- }
- }
- Var JavascriptRegExp::EntrySymbolMatch(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- Assert(!(callInfo.Flags & CallFlags_New));
- ScriptContext* scriptContext = function->GetScriptContext();
- CHAKRATEL_LANGSTATS_INC_DATACOUNT(ES6_RegexSymbolMatch);
- PCWSTR const varName = _u("RegExp.prototype[Symbol.match]");
- RecyclableObject *thisObj = GetThisObject(args, varName, scriptContext);
- JavascriptString* string = GetFirstStringArg(args, scriptContext);
- return RegexHelper::RegexMatch(
- scriptContext,
- thisObj,
- string,
- RegexHelper::IsResultNotUsed(callInfo.Flags));
- }
- bool JavascriptRegExp::HasOriginalRegExType(RecyclableObject* instance)
- {
- JavascriptLibrary* library = instance->GetLibrary();
- if (instance->GetType() != library->GetRegexType())
- {
- return false;
- }
- DynamicObject* regexPrototype = library->GetRegExpPrototype();
- return JavascriptOperators::GetPrototype(instance) == regexPrototype
- && regexPrototype->GetType() == library->GetRegexPrototypeType();
- }
- bool JavascriptRegExp::HasObservableConstructor(DynamicObject* regexPrototype)
- {
- JavascriptLibrary* library = regexPrototype->GetLibrary();
- return regexPrototype->GetSlot(library->GetRegexConstructorSlotIndex()) != library->GetRegExpConstructor();
- }
- bool JavascriptRegExp::HasObservableExec(DynamicObject* regexPrototype)
- {
- JavascriptLibrary* library = regexPrototype->GetLibrary();
- return regexPrototype->GetSlot(library->GetRegexExecSlotIndex()) != library->GetRegexExecFunction();
- }
- bool JavascriptRegExp::HasObservableFlags(DynamicObject* regexPrototype)
- {
- JavascriptLibrary* library = regexPrototype->GetLibrary();
- return regexPrototype->GetScriptContext()->GetConfig()->IsES6RegExPrototypePropertiesEnabled()
- && regexPrototype->GetSlot(library->GetRegexFlagsGetterSlotIndex()) != library->GetRegexFlagsGetterFunction();
- }
- bool JavascriptRegExp::HasObservableGlobalFlag(DynamicObject* regexPrototype)
- {
- JavascriptLibrary* library = regexPrototype->GetLibrary();
- return regexPrototype->GetScriptContext()->GetConfig()->IsES6RegExPrototypePropertiesEnabled()
- && regexPrototype->GetSlot(library->GetRegexGlobalGetterSlotIndex()) != library->GetRegexGlobalGetterFunction();
- }
- bool JavascriptRegExp::HasObservableUnicodeFlag(DynamicObject* regexPrototype)
- {
- const ScriptConfiguration* scriptConfig = regexPrototype->GetScriptContext()->GetConfig();
- JavascriptLibrary* library = regexPrototype->GetLibrary();
- return scriptConfig->IsES6UnicodeExtensionsEnabled()
- && scriptConfig->IsES6RegExPrototypePropertiesEnabled()
- && regexPrototype->GetSlot(library->GetRegexUnicodeGetterSlotIndex()) != library->GetRegexUnicodeGetterFunction();
- }
- Var JavascriptRegExp::EntrySymbolReplace(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- Assert(!(callInfo.Flags & CallFlags_New));
- ScriptContext* scriptContext = function->GetScriptContext();
- CHAKRATEL_LANGSTATS_INC_DATACOUNT(ES6_RegexSymbolReplace);
- PCWSTR varName = _u("RegExp.prototype[Symbol.replace]");
- RecyclableObject* thisObj = GetThisObject(args, varName, scriptContext);
- JavascriptString* string = GetFirstStringArg(args, scriptContext);
- Var replaceValue = (args.Info.Count > 2) ? args[2] : scriptContext->GetLibrary()->GetUndefined();
- if (JavascriptFunction::Is(replaceValue))
- {
- JavascriptFunction* replaceFunction = JavascriptFunction::FromVar(replaceValue);
- return RegexHelper::RegexReplaceFunction(scriptContext, thisObj, string, replaceFunction);
- }
- else
- {
- JavascriptString* replaceString = JavascriptConversion::ToString(replaceValue, scriptContext);
- return RegexHelper::RegexReplace(
- scriptContext,
- thisObj,
- string,
- replaceString,
- RegexHelper::IsResultNotUsed(callInfo.Flags));
- }
- }
- Var JavascriptRegExp::EntrySymbolSearch(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- Assert(!(callInfo.Flags & CallFlags_New));
- ScriptContext* scriptContext = function->GetScriptContext();
- CHAKRATEL_LANGSTATS_INC_DATACOUNT(ES6_RegexSymbolSearch);
- PCWSTR const varName = _u("RegExp.prototype[Symbol.search]");
- RecyclableObject *thisObj = GetThisObject(args, varName, scriptContext);
- Var regEx = args[0];
- JavascriptString* string = GetFirstStringArg(args, scriptContext);
- Var previousLastIndex = JavascriptOperators::GetProperty(thisObj, PropertyIds::lastIndex, scriptContext);
- SetLastIndexProperty(regEx, TaggedInt::ToVarUnchecked(0), scriptContext);
- Var result = CallExec(thisObj, string, varName, scriptContext);
- SetLastIndexProperty(regEx, previousLastIndex, scriptContext);
- return JavascriptOperators::IsNull(result)
- ? TaggedInt::ToVarUnchecked(-1)
- : JavascriptOperators::GetProperty(RecyclableObject::FromVar(result), PropertyIds::index, scriptContext);
- }
- Var JavascriptRegExp::EntrySymbolSplit(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- Assert(!(callInfo.Flags & CallFlags_New));
- ScriptContext* scriptContext = function->GetScriptContext();
- CHAKRATEL_LANGSTATS_INC_DATACOUNT(ES6_RegexSymbolSplit);
- RecyclableObject *thisObj = GetThisObject(args, _u("RegExp.prototype[Symbol.match]"), scriptContext);
- JavascriptString* string = GetFirstStringArg(args, scriptContext);
- // TODO: SPEC DEVIATION
- //
- // In RegexHelper::RegexSplit, we check if RegExp properties are overridden in order to determine
- // if the algorithm is observable. If it is, we go through the new ES6 algorithm, but otherwise, we
- // run the faster ES5 version.
- //
- // According to the spec, we're supposed to process "limit" after we use some of the RegExp properties.
- // However, there doesn't seem to be any reason why "limit" processing can't be pulled above the rest
- // in the spec. Therefore, we should see if such a spec update is OK. If not, this would have to be
- // moved to its correct place in the code.
- uint32 limit = (args.Info.Count < 3 || JavascriptOperators::IsUndefinedObject(args[2], scriptContext))
- ? UINT_MAX
- : JavascriptConversion::ToUInt32(args[2], scriptContext);
- return RegexHelper::RegexSplit(
- scriptContext,
- thisObj,
- string,
- limit,
- RegexHelper::IsResultNotUsed(callInfo.Flags));
- }
- Var JavascriptRegExp::CallExec(RecyclableObject* thisObj, JavascriptString* string, PCWSTR varName, ScriptContext* scriptContext)
- {
- Var exec = JavascriptOperators::GetProperty(thisObj, PropertyIds::exec, scriptContext);
- if (JavascriptConversion::IsCallable(exec))
- {
- RecyclableObject* execFn = RecyclableObject::FromVar(exec);
- Var result = CALL_FUNCTION(scriptContext->GetThreadContext(), execFn, CallInfo(CallFlags_Value, 2), thisObj, string);
- if (!JavascriptOperators::IsObjectOrNull(result))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_RegExpExecInvalidReturnType, varName);
- }
- return result;
- }
- JavascriptRegExp* regExObj = ToRegExp(thisObj, varName, scriptContext);
- return RegexHelper::RegexExec(scriptContext, regExObj, string, false);
- }
- UnifiedRegex::RegexFlags JavascriptRegExp::SetRegexFlag(
- PropertyId propertyId,
- UnifiedRegex::RegexFlags flags,
- UnifiedRegex::RegexFlags flag,
- ScriptContext* scriptContext)
- {
- bool isEnabled = JavascriptConversion::ToBool(
- JavascriptOperators::GetProperty(this, propertyId, scriptContext),
- scriptContext);
- return isEnabled
- ? static_cast<UnifiedRegex::RegexFlags>(flags | flag)
- : static_cast<UnifiedRegex::RegexFlags>(flags & ~flag);
- }
- Var JavascriptRegExp::EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ARGUMENTS(args, callInfo);
- Assert(args.Info.Count > 0);
- return args[0];
- }
- Var JavascriptRegExp::EntryGetterFlags(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- Assert(!(callInfo.Flags & CallFlags_New));
- ScriptContext* scriptContext = function->GetScriptContext();
- RecyclableObject *thisObj = GetThisObject(args, _u("RegExp.prototype.flags"), scriptContext);
- Var flags;
- BEGIN_TEMP_ALLOCATOR(tempAlloc, scriptContext, _u("JavascriptRegExp"))
- {
- StringBuilder<ArenaAllocator> bs(tempAlloc, 5);
- #define APPEND_FLAG(propertyId, flag) AppendFlagForFlagsProperty(&bs, thisObj, propertyId, flag, scriptContext)
- // If you change the order of the flags, don't forget to change it in GetOptions() and ToString() too.
- APPEND_FLAG(PropertyIds::global, _u('g'));
- APPEND_FLAG(PropertyIds::ignoreCase, _u('i'));
- APPEND_FLAG(PropertyIds::multiline, _u('m'));
- ScriptConfiguration const * scriptConfig = scriptContext->GetConfig();
- if (scriptConfig->IsES6UnicodeExtensionsEnabled())
- {
- APPEND_FLAG(PropertyIds::unicode, _u('u'));
- }
- if (scriptConfig->IsES6RegExStickyEnabled())
- {
- APPEND_FLAG(PropertyIds::sticky, _u('y'));
- }
- #undef APPEND_FLAG
- flags = Js::JavascriptString::NewCopyBuffer(bs.Detach(), bs.Count(), scriptContext);
- }
- END_TEMP_ALLOCATOR(tempAlloc, scriptContext);
- return flags;
- }
- void JavascriptRegExp::AppendFlagForFlagsProperty(
- StringBuilder<ArenaAllocator>* builder,
- RecyclableObject* thisObj,
- PropertyId propertyId,
- char16 flag,
- ScriptContext* scriptContext)
- {
- Var propertyValue = JavascriptOperators::GetProperty(thisObj, propertyId, scriptContext);
- if (JavascriptConversion::ToBoolean(propertyValue, scriptContext))
- {
- builder->Append(flag);
- }
- }
- Var JavascriptRegExp::EntryGetterOptions(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- Assert(!(callInfo.Flags & CallFlags_New));
- ScriptContext* scriptContext = function->GetScriptContext();
- if (ShouldApplyPrototypeWebWorkaround(args, scriptContext))
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- return GetJavascriptRegExp(args, _u("RegExp.prototype.options"), scriptContext)->GetOptions();
- }
- Var JavascriptRegExp::GetOptions()
- {
- Var options;
- ScriptContext* scriptContext = this->GetLibrary()->GetScriptContext();
- BEGIN_TEMP_ALLOCATOR(tempAlloc, scriptContext, _u("JavascriptRegExp"))
- {
- StringBuilder<ArenaAllocator> bs(tempAlloc, 4);
- // If you change the order of the flags, don't forget to change it in EntryGetterFlags() and ToString() too.
- if(GetPattern()->IsGlobal())
- {
- bs.Append(_u('g'));
- }
- if(GetPattern()->IsIgnoreCase())
- {
- bs.Append(_u('i'));
- }
- if(GetPattern()->IsMultiline())
- {
- bs.Append(_u('m'));
- }
- if (GetPattern()->IsUnicode())
- {
- bs.Append(_u('u'));
- }
- if (GetPattern()->IsSticky())
- {
- bs.Append(_u('y'));
- }
- options = Js::JavascriptString::NewCopyBuffer(bs.Detach(), bs.Count(), scriptContext);
- }
- END_TEMP_ALLOCATOR(tempAlloc, scriptContext);
- return options;
- }
- Var JavascriptRegExp::EntryGetterSource(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- Assert(!(callInfo.Flags & CallFlags_New));
- ScriptContext* scriptContext = function->GetScriptContext();
- if (ShouldApplyPrototypeWebWorkaround(args, scriptContext))
- {
- return JavascriptString::NewCopyBuffer(_u("(?:)"), 4, scriptContext);
- }
- return GetJavascriptRegExp(args, _u("RegExp.prototype.source"), scriptContext)->ToString(true);
- }
- #define DEFINE_FLAG_GETTER(methodName, propertyName, patternMethodName) \
- Var JavascriptRegExp::##methodName##(RecyclableObject* function, CallInfo callInfo, ...) \
- { \
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); \
- ARGUMENTS(args, callInfo); \
- Assert(!(callInfo.Flags & CallFlags_New)); \
- \
- ScriptContext* scriptContext = function->GetScriptContext(); \
- if (ShouldApplyPrototypeWebWorkaround(args, scriptContext)) \
- {\
- return scriptContext->GetLibrary()->GetUndefined(); \
- }\
- \
- JavascriptRegExp* pRegEx = GetJavascriptRegExp(args, _u("RegExp.prototype.") _u(#propertyName), scriptContext); \
- return pRegEx->GetLibrary()->CreateBoolean(pRegEx->GetPattern()->##patternMethodName##()); \
- }
- DEFINE_FLAG_GETTER(EntryGetterGlobal, global, IsGlobal)
- DEFINE_FLAG_GETTER(EntryGetterIgnoreCase, ignoreCase, IsIgnoreCase)
- DEFINE_FLAG_GETTER(EntryGetterMultiline, multiline, IsMultiline)
- DEFINE_FLAG_GETTER(EntryGetterSticky, sticky, IsSticky)
- DEFINE_FLAG_GETTER(EntryGetterUnicode, unicode, IsUnicode)
- JavascriptRegExp * JavascriptRegExp::BoxStackInstance(JavascriptRegExp * instance)
- {
- Assert(ThreadContext::IsOnStack(instance));
- // On the stack, the we reserved a pointer before the object as to store the boxed value
- JavascriptRegExp ** boxedInstanceRef = ((JavascriptRegExp **)instance) - 1;
- JavascriptRegExp * boxedInstance = *boxedInstanceRef;
- if (boxedInstance)
- {
- return boxedInstance;
- }
- Assert(instance->GetTypeHandler()->GetInlineSlotsSize() == 0);
- boxedInstance = RecyclerNew(instance->GetRecycler(), JavascriptRegExp, instance);
- *boxedInstanceRef = boxedInstance;
- return boxedInstance;
- }
- // Both 'unicode' and 'sticky' special properties could be controlled via config
- // flags. Below, 'sticky' is listed after 'unicode' (for no special reason).
- // When the 'unicode' config flag is disabled, we want 'unicode' to be excluded
- // from the list, but we still want 'sticky' to be included depending on its
- // config flag. That's the reason why we have two lists for the special property
- // IDs.
- #define DEFAULT_SPECIAL_PROPERTY_IDS \
- PropertyIds::lastIndex, \
- PropertyIds::global, \
- PropertyIds::multiline, \
- PropertyIds::ignoreCase, \
- PropertyIds::source, \
- PropertyIds::options
- PropertyId const JavascriptRegExp::specialPropertyIdsAll[] =
- {
- DEFAULT_SPECIAL_PROPERTY_IDS,
- PropertyIds::unicode,
- PropertyIds::sticky
- };
- PropertyId const JavascriptRegExp::specialPropertyIdsWithoutUnicode[] =
- {
- DEFAULT_SPECIAL_PROPERTY_IDS,
- PropertyIds::sticky
- };
- PropertyQueryFlags JavascriptRegExp::HasPropertyQuery(PropertyId propertyId)
- {
- const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
- #define HAS_PROPERTY(ownProperty) \
- return (ownProperty ? PropertyQueryFlags::Property_Found : DynamicObject::HasPropertyQuery(propertyId));
- switch (propertyId)
- {
- case PropertyIds::lastIndex:
- return PropertyQueryFlags::Property_Found;
- case PropertyIds::global:
- case PropertyIds::multiline:
- case PropertyIds::ignoreCase:
- case PropertyIds::source:
- case PropertyIds::options:
- HAS_PROPERTY(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- case PropertyIds::unicode:
- HAS_PROPERTY(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled())
- case PropertyIds::sticky:
- HAS_PROPERTY(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled())
- default:
- return DynamicObject::HasPropertyQuery(propertyId);
- }
- #undef HAS_PROPERTY
- }
- PropertyQueryFlags JavascriptRegExp::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- return JavascriptRegExp::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
- }
- PropertyQueryFlags JavascriptRegExp::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- BOOL result;
- if (GetPropertyBuiltIns(propertyId, value, &result))
- {
- return JavascriptConversion::BooleanToPropertyQueryFlags(result);
- }
- return DynamicObject::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
- }
- PropertyQueryFlags JavascriptRegExp::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- BOOL result;
- PropertyRecord const* propertyRecord;
- this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
- if (propertyRecord != nullptr && GetPropertyBuiltIns(propertyRecord->GetPropertyId(), value, &result))
- {
- return JavascriptConversion::BooleanToPropertyQueryFlags(result);
- }
- return DynamicObject::GetPropertyQuery(originalInstance, propertyNameString, value, info, requestContext);
- }
- bool JavascriptRegExp::GetPropertyBuiltIns(PropertyId propertyId, Var* value, BOOL* result)
- {
- const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
- #define GET_FLAG(patternMethod) \
- if (!scriptConfig->IsES6RegExPrototypePropertiesEnabled()) \
- { \
- *value = this->GetLibrary()->CreateBoolean(this->GetPattern()->##patternMethod##()); \
- *result = true; \
- return true; \
- } \
- else \
- { \
- return false; \
- }
- switch (propertyId)
- {
- case PropertyIds::lastIndex:
- if (this->lastIndexVar == nullptr)
- {
- Assert(lastIndexOrFlag <= MaxCharCount);
- this->lastIndexVar = JavascriptNumber::ToVar(lastIndexOrFlag, GetScriptContext());
- }
- *value = this->lastIndexVar;
- *result = true;
- return true;
- case PropertyIds::global:
- GET_FLAG(IsGlobal)
- case PropertyIds::multiline:
- GET_FLAG(IsMultiline)
- case PropertyIds::ignoreCase:
- GET_FLAG(IsIgnoreCase)
- case PropertyIds::unicode:
- GET_FLAG(IsUnicode)
- case PropertyIds::sticky:
- GET_FLAG(IsSticky)
- case PropertyIds::source:
- if (!scriptConfig->IsES6RegExPrototypePropertiesEnabled())
- {
- *value = this->ToString(true);
- *result = true;
- return true;
- }
- else
- {
- return false;
- }
- case PropertyIds::options:
- if (!scriptConfig->IsES6RegExPrototypePropertiesEnabled())
- {
- *value = GetOptions();
- *result = true;
- return true;
- }
- else
- {
- return false;
- }
- default:
- return false;
- }
- #undef GET_FLAG
- }
- BOOL JavascriptRegExp::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
- {
- BOOL result;
- if (SetPropertyBuiltIns(propertyId, value, flags, &result))
- {
- return result;
- }
- return DynamicObject::SetProperty(propertyId, value, flags, info);
- }
- BOOL JavascriptRegExp::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
- {
- BOOL result;
- PropertyRecord const * propertyRecord;
- this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
- if (propertyRecord != nullptr && SetPropertyBuiltIns(propertyRecord->GetPropertyId(), value, flags, &result))
- {
- return result;
- }
- return DynamicObject::SetProperty(propertyNameString, value, flags, info);
- }
- bool JavascriptRegExp::SetPropertyBuiltIns(PropertyId propertyId, Var value, PropertyOperationFlags flags, BOOL* result)
- {
- const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
- #define SET_PROPERTY(ownProperty) \
- if (ownProperty) \
- { \
- JavascriptError::ThrowCantAssignIfStrictMode(flags, this->GetScriptContext()); \
- *result = false; \
- return true; \
- } \
- return false;
- switch (propertyId)
- {
- case PropertyIds::lastIndex:
- this->lastIndexVar = value;
- lastIndexOrFlag = NotCachedValue;
- *result = true;
- return true;
- case PropertyIds::global:
- case PropertyIds::multiline:
- case PropertyIds::ignoreCase:
- case PropertyIds::source:
- case PropertyIds::options:
- SET_PROPERTY(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- case PropertyIds::unicode:
- SET_PROPERTY(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- case PropertyIds::sticky:
- SET_PROPERTY(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- default:
- return false;
- }
- #undef SET_PROPERTY
- }
- BOOL JavascriptRegExp::InitProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
- {
- return SetProperty(propertyId, value, flags, info);
- }
- BOOL JavascriptRegExp::DeleteProperty(PropertyId propertyId, PropertyOperationFlags propertyOperationFlags)
- {
- const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
- #define DELETE_PROPERTY(ownProperty) \
- if (ownProperty) \
- { \
- JavascriptError::ThrowCantDeleteIfStrictMode(propertyOperationFlags, this->GetScriptContext(), this->GetScriptContext()->GetPropertyName(propertyId)->GetBuffer()); \
- return false; \
- } \
- return DynamicObject::DeleteProperty(propertyId, propertyOperationFlags);
- switch (propertyId)
- {
- case PropertyIds::lastIndex:
- DELETE_PROPERTY(true);
- case PropertyIds::global:
- case PropertyIds::multiline:
- case PropertyIds::ignoreCase:
- case PropertyIds::source:
- case PropertyIds::options:
- DELETE_PROPERTY(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- case PropertyIds::unicode:
- DELETE_PROPERTY(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- case PropertyIds::sticky:
- DELETE_PROPERTY(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- default:
- return DynamicObject::DeleteProperty(propertyId, propertyOperationFlags);
- }
- #undef DELETE_PROPERTY
- }
- BOOL JavascriptRegExp::DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags)
- {
- const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
- JsUtil::CharacterBuffer<WCHAR> propertyName(propertyNameString->GetString(), propertyNameString->GetLength());
- #define DELETE_PROPERTY(ownProperty) \
- if (ownProperty) \
- { \
- JavascriptError::ThrowCantDeleteIfStrictMode(flags, this->GetScriptContext(), propertyNameString->GetString()); \
- return false; \
- } \
- return DynamicObject::DeleteProperty(propertyNameString, flags);
- if (BuiltInPropertyRecords::lastIndex.Equals(propertyName))
- {
- DELETE_PROPERTY(true);
- }
- else if (BuiltInPropertyRecords::global.Equals(propertyName)
- || BuiltInPropertyRecords::multiline.Equals(propertyName)
- || BuiltInPropertyRecords::ignoreCase.Equals(propertyName)
- || BuiltInPropertyRecords::source.Equals(propertyName)
- || BuiltInPropertyRecords::options.Equals(propertyName))
- {
- DELETE_PROPERTY(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- }
- else if (BuiltInPropertyRecords::unicode.Equals(propertyName))
- {
- DELETE_PROPERTY(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- }
- else if (BuiltInPropertyRecords::sticky.Equals(propertyName))
- {
- DELETE_PROPERTY(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- }
- else
- {
- return DynamicObject::DeleteProperty(propertyNameString, flags);
- }
- #undef DELETE_PROPERTY
- }
- DescriptorFlags JavascriptRegExp::GetSetter(PropertyId propertyId, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- DescriptorFlags result;
- if (GetSetterBuiltIns(propertyId, info, &result))
- {
- return result;
- }
- return DynamicObject::GetSetter(propertyId, setterValue, info, requestContext);
- }
- DescriptorFlags JavascriptRegExp::GetSetter(JavascriptString* propertyNameString, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- DescriptorFlags result;
- PropertyRecord const * propertyRecord;
- this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
- if (propertyRecord != nullptr && GetSetterBuiltIns(propertyRecord->GetPropertyId(), info, &result))
- {
- return result;
- }
- return DynamicObject::GetSetter(propertyNameString, setterValue, info, requestContext);
- }
- bool JavascriptRegExp::GetSetterBuiltIns(PropertyId propertyId, PropertyValueInfo* info, DescriptorFlags* result)
- {
- const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
- #define GET_SETTER(ownProperty) \
- if (ownProperty) \
- { \
- PropertyValueInfo::SetNoCache(info, this); \
- *result = JavascriptRegExp::IsWritable(propertyId) ? WritableData : Data; \
- return true; \
- } \
- return false;
- switch (propertyId)
- {
- case PropertyIds::lastIndex:
- GET_SETTER(true);
- case PropertyIds::global:
- case PropertyIds::multiline:
- case PropertyIds::ignoreCase:
- case PropertyIds::source:
- case PropertyIds::options:
- GET_SETTER(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- case PropertyIds::unicode:
- GET_SETTER(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- case PropertyIds::sticky:
- GET_SETTER(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- default:
- return false;
- }
- #undef GET_SETTER
- }
- BOOL JavascriptRegExp::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
- {
- Js::InternalString str = pattern->GetSource();
- stringBuilder->Append(str.GetBuffer(), str.GetLength());
- return TRUE;
- }
- BOOL JavascriptRegExp::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
- {
- stringBuilder->AppendCppLiteral(JS_DIAG_TYPE_JavascriptRegExp);
- return TRUE;
- }
- BOOL JavascriptRegExp::IsEnumerable(PropertyId propertyId)
- {
- const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
- #define IS_ENUMERABLE(ownProperty) \
- return (ownProperty ? false : DynamicObject::IsEnumerable(propertyId));
- switch (propertyId)
- {
- case PropertyIds::lastIndex:
- return false;
- case PropertyIds::global:
- case PropertyIds::multiline:
- case PropertyIds::ignoreCase:
- case PropertyIds::source:
- case PropertyIds::options:
- IS_ENUMERABLE(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- case PropertyIds::unicode:
- IS_ENUMERABLE(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- case PropertyIds::sticky:
- IS_ENUMERABLE(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- default:
- return DynamicObject::IsEnumerable(propertyId);
- }
- #undef IS_ENUMERABLE
- }
- BOOL JavascriptRegExp::IsConfigurable(PropertyId propertyId)
- {
- const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
- #define IS_CONFIGURABLE(ownProperty) \
- return (ownProperty ? false : DynamicObject::IsConfigurable(propertyId));
- switch (propertyId)
- {
- case PropertyIds::lastIndex:
- return false;
- case PropertyIds::global:
- case PropertyIds::multiline:
- case PropertyIds::ignoreCase:
- case PropertyIds::source:
- case PropertyIds::options:
- IS_CONFIGURABLE(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- case PropertyIds::unicode:
- IS_CONFIGURABLE(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- case PropertyIds::sticky:
- IS_CONFIGURABLE(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- default:
- return DynamicObject::IsConfigurable(propertyId);
- }
- #undef IS_CONFIGURABLE
- }
- BOOL JavascriptRegExp::IsWritable(PropertyId propertyId)
- {
- const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
- #define IS_WRITABLE(ownProperty) \
- return (ownProperty ? false : DynamicObject::IsWritable(propertyId));
- switch (propertyId)
- {
- case PropertyIds::lastIndex:
- return true;
- case PropertyIds::global:
- case PropertyIds::multiline:
- case PropertyIds::ignoreCase:
- case PropertyIds::source:
- case PropertyIds::options:
- IS_WRITABLE(!scriptConfig->IsES6RegExPrototypePropertiesEnabled())
- case PropertyIds::unicode:
- IS_WRITABLE(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- case PropertyIds::sticky:
- IS_WRITABLE(scriptConfig->IsES6RegExStickyEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- default:
- return DynamicObject::IsWritable(propertyId);
- }
- #undef IS_WRITABLE
- }
- BOOL JavascriptRegExp::GetSpecialPropertyName(uint32 index, JavascriptString ** propertyName, ScriptContext * requestContext)
- {
- uint length = GetSpecialPropertyCount();
- if (index < length)
- {
- *propertyName = requestContext->GetPropertyString(GetSpecialPropertyIdsInlined()[index]);
- return true;
- }
- return false;
- }
- // Returns the number of special non-enumerable properties this type has.
- uint JavascriptRegExp::GetSpecialPropertyCount() const
- {
- if (GetScriptContext()->GetConfig()->IsES6RegExPrototypePropertiesEnabled())
- {
- return 1; // lastIndex
- }
- uint specialPropertyCount = defaultSpecialPropertyIdsCount;
- if (GetScriptContext()->GetConfig()->IsES6UnicodeExtensionsEnabled())
- {
- specialPropertyCount += 1;
- }
- if (GetScriptContext()->GetConfig()->IsES6RegExStickyEnabled())
- {
- specialPropertyCount += 1;
- }
- return specialPropertyCount;
- }
- // Returns the list of special non-enumerable properties for the type.
- PropertyId const * JavascriptRegExp::GetSpecialPropertyIds() const
- {
- return GetSpecialPropertyIdsInlined();
- }
- inline PropertyId const * JavascriptRegExp::GetSpecialPropertyIdsInlined() const
- {
- return GetScriptContext()->GetConfig()->IsES6UnicodeExtensionsEnabled()
- ? specialPropertyIdsAll
- : specialPropertyIdsWithoutUnicode;
- }
- #if ENABLE_TTD
- TTD::NSSnapObjects::SnapObjectType JavascriptRegExp::GetSnapTag_TTD() const
- {
- return TTD::NSSnapObjects::SnapObjectType::SnapRegexObject;
- }
- void JavascriptRegExp::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
- {
- TTD::NSSnapObjects::SnapRegexInfo* sri = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapRegexInfo>();
- UnifiedRegex::RegexPattern* pattern = this->pattern;
- alloc.CopyStringIntoWLength(pattern->GetSource().GetBuffer(), pattern->GetSource().GetLength(), sri->RegexStr);
- //split regex should be automatically generated from regex string and flags so no need to exttract it as well
- sri->Flags = this->GetFlags();
- sri->LastIndexVar = TTD_CONVERT_JSVAR_TO_TTDVAR(this->lastIndexVar);
- sri->LastIndexOrFlag = this->lastIndexOrFlag;
- TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapRegexInfo*, TTD::NSSnapObjects::SnapObjectType::SnapRegexObject>(objData, sri);
- }
- void JavascriptRegExp::SetLastIndexInfo_TTD(CharCount lastIndex, Js::Var lastVar)
- {
- this->lastIndexOrFlag = lastIndex;
- this->lastIndexVar = lastVar;
- }
- #endif
- } // namespace Js
|