| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656 |
- //-------------------------------------------------------------------------------------------------------
- // 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"
- using 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());
- Assert(!this->GetType()->ThisAndPrototypesHaveNoSpecialProperties());
- #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, bool deepCopy) :
- DynamicObject(instance, deepCopy),
- pattern(instance->GetPattern()),
- splitPattern(instance->GetSplitPattern()),
- lastIndexVar(instance->lastIndexVar),
- lastIndexOrFlag(instance->lastIndexOrFlag)
- {
- // For boxing stack instance
- Assert(ThreadContext::IsOnStack(instance));
- // These members should never be on the stack and thus never need to be deep copied
- Assert(!ThreadContext::IsOnStack(instance->GetPattern()));
- Assert(!ThreadContext::IsOnStack(instance->GetSplitPattern()));
- Assert(!ThreadContext::IsOnStack(instance->lastIndexVar));
- }
- // 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(
- VarTo<RecyclableObject>(aValue),
- PropertyIds::_symbolMatch,
- scriptContext);
- if (!JavascriptOperators::IsUndefined(symbolMatchProperty))
- {
- return JavascriptConversion::ToBool(symbolMatchProperty, scriptContext);
- }
- }
- return VarIs<JavascriptRegExp>(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,
- VarTo<RecyclableObject>(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)
- {
- JavascriptRegExp * regExp = JavascriptOperators::TryFromVar<JavascriptRegExp>(var);
- if (regExp)
- {
- return regExp;
- }
- if (JavascriptOperators::GetTypeId(var) == TypeIds_HostDispatch)
- {
- TypeId remoteTypeId = TypeIds_Limit;
- RecyclableObject* reclObj = UnsafeVarTo<RecyclableObject>(var);
- if (reclObj->GetRemoteTypeId(&remoteTypeId) && 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 VarTo<RecyclableObject>(args[0]);
- }
- JavascriptString* JavascriptRegExp::GetFirstStringArg(Arguments& args, ScriptContext* scriptContext)
- {
- if (args.Info.Count == 1)
- {
- return scriptContext->GetLibrary()->GetUndefinedDisplayString();
- }
- else
- {
- JavascriptString *jsString = JavascriptOperators::TryFromVar<JavascriptString>(args[1]);
- if (jsString)
- {
- return jsString;
- }
- 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 = args.HasNewTarget() ? args.Values[args.Info.Count] : function;
- bool isCtorSuperCall = JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
- 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 = VarTo<RecyclableObject>(args[1]);
- if (!(callInfo.Flags & CallFlags_New) &&
- (callInfo.Count == 2 || JavascriptOperators::IsUndefinedObject(args[2])) &&
- 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 (VarIs<JavascriptRegExp>(regexLikeObj))
- {
- JavascriptRegExp* source = VarTo<JavascriptRegExp>(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]))
- {
- 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(VarTo<RecyclableObject>(newTarget), regex, nullptr, scriptContext) :
- regex;
- }
- UnifiedRegex::RegexPattern* JavascriptRegExp::CreatePattern(Var aValue, Var options, ScriptContext *scriptContext)
- {
- JavascriptString * strBody;
- if (VarIs<JavascriptString>(aValue))
- {
- strBody = VarTo<JavascriptString>(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))
- {
- if (VarIs<JavascriptString>(options))
- {
- strOptions = VarTo<JavascriptString>(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)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_CoerseRegex);
- // 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 (VarIs<JavascriptRegExp>(aValue))
- {
- return VarTo<JavascriptRegExp>(aValue);
- }
- else
- {
- return CreateRegExNoCoerce(aValue, options, scriptContext);
- }
- JIT_HELPER_END(Op_CoerseRegex);
- }
- 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->IsDotAll())
- {
- builder->AppendChars(_u('s'));
- }
- 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 (VarIs<JavascriptRegExp>(args[1]))
- {
- JavascriptRegExp* source = VarTo<JavascriptRegExp>(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 (VarIs<JavascriptString>(args[1]))
- {
- strBody = VarTo<JavascriptString>(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]))
- {
- if (VarIs<JavascriptString>(args[2]))
- {
- strOptions = VarTo<JavascriptString>(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(UnifiedRegex::RegexPattern* aCompiledRegex, ScriptContext* scriptContext)
- {
- JavascriptRegExp * pNewInstance =
- RecyclerNew(scriptContext->GetRecycler(), JavascriptRegExp, 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_LANGFEATURECOUNT(ES6, RegexSymbolMatch, scriptContext);
- 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_LANGFEATURECOUNT(ES6, RegexSymbolReplace, scriptContext);
- 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 (JavascriptConversion::IsCallable(replaceValue))
- {
- RecyclableObject* replaceFunction = VarTo<RecyclableObject>(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_LANGFEATURECOUNT(ES6, RegexSymbolSearch, scriptContext);
- 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(VarTo<RecyclableObject>(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_LANGFEATURECOUNT(ES6, RegexSymbolSplit, scriptContext);
- 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]))
- ? 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 = UnsafeVarTo<RecyclableObject>(exec);
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- Var result = threadContext->ExecuteImplicitCall(execFn, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return 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->IsES2018RegExDotAllEnabled())
- {
- APPEND_FLAG(PropertyIds::dotAll, _u('s'));
- }
- 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()->IsDotAll())
- {
- bs.Append(_u('s'));
- }
- 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) || args[0] == scriptContext->GetLibrary()->GetRegExpPrototype()) \
- {\
- 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)
- DEFINE_FLAG_GETTER(EntryGetterDotAll, dotAll, IsDotAll)
- JavascriptRegExp * JavascriptRegExp::BoxStackInstance(JavascriptRegExp * instance, bool deepCopy)
- {
- 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, deepCopy);
- *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::dotAll,
- PropertyIds::sticky
- };
- PropertyId const JavascriptRegExp::specialPropertyIdsWithoutUnicode[] =
- {
- DEFAULT_SPECIAL_PROPERTY_IDS,
- PropertyIds::dotAll,
- PropertyIds::sticky
- };
- PropertyId const JavascriptRegExp::specialPropertyIdsWithoutDotAll[] =
- {
- DEFAULT_SPECIAL_PROPERTY_IDS,
- PropertyIds::unicode,
- PropertyIds::sticky
- };
- PropertyId const JavascriptRegExp::specialPropertyIdsWithoutDotAllOrUnicode[] =
- {
- DEFAULT_SPECIAL_PROPERTY_IDS,
- PropertyIds::sticky
- };
- PropertyQueryFlags JavascriptRegExp::HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info)
- {
- const ScriptConfiguration* scriptConfig = this->GetScriptContext()->GetConfig();
- #define HAS_PROPERTY(ownProperty) \
- return (ownProperty ? PropertyQueryFlags::Property_Found : DynamicObject::HasPropertyQuery(propertyId, info));
- 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::dotAll:
- HAS_PROPERTY(scriptConfig->IsES2018RegExDotAllEnabled() && !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, info);
- }
- #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::dotAll:
- GET_FLAG(IsDotAll)
- 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::dotAll:
- SET_PROPERTY(scriptConfig->IsES2018RegExDotAllEnabled() && !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::dotAll:
- DELETE_PROPERTY(scriptConfig->IsES2018RegExDotAllEnabled() && !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();
- #define DELETE_PROPERTY(ownProperty) \
- if (ownProperty) \
- { \
- JavascriptError::ThrowCantDeleteIfStrictMode(flags, this->GetScriptContext(), propertyNameString->GetString()); \
- return false; \
- } \
- return DynamicObject::DeleteProperty(propertyNameString, flags);
- if (BuiltInPropertyRecords::lastIndex.Equals(propertyNameString))
- {
- DELETE_PROPERTY(true);
- }
- else if (BuiltInPropertyRecords::global.Equals(propertyNameString)
- || BuiltInPropertyRecords::multiline.Equals(propertyNameString)
- || BuiltInPropertyRecords::ignoreCase.Equals(propertyNameString)
- || BuiltInPropertyRecords::source.Equals(propertyNameString)
- || BuiltInPropertyRecords::options.Equals(propertyNameString))
- {
- DELETE_PROPERTY(!scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- }
- else if (BuiltInPropertyRecords::unicode.Equals(propertyNameString))
- {
- DELETE_PROPERTY(scriptConfig->IsES6UnicodeExtensionsEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- }
- else if (BuiltInPropertyRecords::dotAll.Equals(propertyNameString))
- {
- DELETE_PROPERTY(scriptConfig->IsES2018RegExDotAllEnabled() && !scriptConfig->IsES6RegExPrototypePropertiesEnabled());
- }
- else if (BuiltInPropertyRecords::sticky.Equals(propertyNameString))
- {
- 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::dotAll:
- GET_SETTER(scriptConfig->IsES2018RegExDotAllEnabled() && !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::dotAll:
- IS_ENUMERABLE(scriptConfig->IsES2018RegExDotAllEnabled() && !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::dotAll:
- IS_CONFIGURABLE(scriptConfig->IsES2018RegExDotAllEnabled() && !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::dotAll:
- IS_WRITABLE(scriptConfig->IsES2018RegExDotAllEnabled() && !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;
- }
- if (GetScriptContext()->GetConfig()->IsES2018RegExDotAllEnabled())
- {
- 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
- {
- const ScriptConfiguration* config = GetScriptContext()->GetConfig();
- if (config->IsES6UnicodeExtensionsEnabled())
- {
- if (config->IsES2018RegExDotAllEnabled())
- {
- return specialPropertyIdsAll;
- }
- else
- {
- return specialPropertyIdsWithoutDotAll;
- }
- }
- else
- {
- if (config->IsES2018RegExDotAllEnabled())
- {
- return specialPropertyIdsWithoutUnicode;
- }
- else
- {
- return specialPropertyIdsWithoutDotAllOrUnicode;
- }
- }
- }
- #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
|