| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935 |
- //-------------------------------------------------------------------------------------------------------
- // 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"
- #include "DataStructures/BigInt.h"
- #include "Library/EngineInterfaceObject.h"
- #include "Library/IntlEngineInterfaceExtensionObject.h"
- #if ENABLE_NATIVE_CODEGEN
- #include "../Backend/JITRecyclableObject.h"
- #endif
- namespace Js
- {
- // White Space characters are defined in ES 2017 Section 11.2 #sec-white-space
- // There are 25 white space characters we need to correctly class.
- // - 6 of these are explicitly specified in ES 2017 Section 11.2 #sec-white-space
- // - 15 of these are Unicode category "Zs" ("Space_Separator") and not explicitly specified above.
- // - Note: In total, 17 of these are Unicode category "Zs".
- // - 4 of these are actually LineTerminator characters.
- // - Note: for various reasons it is convenient to group LineTerminator with Whitespace
- // in the definition of IsWhiteSpaceCharacter.
- // This does not cause problems because of the syntactic nature of LineTerminators
- // and their meaning of ending a line in RegExp.
- // - See: #sec-string.prototype.trim "The definition of white space is the union of WhiteSpace and LineTerminator."
- // Note: ES intentionally excludes characters which have Unicode property "White_Space" but which are not "Zs".
- // See http://www.unicode.org/Public/9.0.0/ucd/UnicodeData.txt for character classes.
- // The 25 white space characters are:
- //0x0009 // <TAB>
- //0x000a // <LF> LineTerminator (LINE FEED)
- //0x000b // <VT>
- //0x000c // <FF>
- //0x000d // <CR> LineTerminator (CARRIAGE RETURN)
- //0x0020 // <SP>
- //0x00a0 // <NBSP>
- //0x1680
- //0x2000
- //0x2001
- //0x2002
- //0x2003
- //0x2004
- //0x2005
- //0x2006
- //0x2007
- //0x2008
- //0x2009
- //0x200a
- //0x2028 // <LS> LineTerminator (LINE SEPARATOR)
- //0x2029 // <PS> LineTerminator (PARAGRAPH SEPARATOR)
- //0x202f
- //0x205f
- //0x3000
- //0xfeff // <ZWNBSP>
- bool IsWhiteSpaceCharacter(char16 ch)
- {
- return ch >= 0x9 &&
- (ch <= 0xd ||
- (ch <= 0x200a &&
- (ch >= 0x2000 || ch == 0x20 || ch == 0xa0 || ch == 0x1680)
- ) ||
- (ch >= 0x2028 &&
- (ch <= 0x2029 || ch == 0x202f || ch == 0x205f || ch == 0x3000 || ch == 0xfeff)
- )
- );
- }
- template <typename T, bool copyBuffer>
- JavascriptString* JavascriptString::NewWithBufferT(const char16 * content, charcount_t cchUseLength, ScriptContext * scriptContext)
- {
- AssertMsg(content != nullptr, "NULL value passed to JavascriptString::New");
- AssertMsg(IsValidCharCount(cchUseLength), "String length will overflow an int");
- switch (cchUseLength)
- {
- case 0:
- return scriptContext->GetLibrary()->GetEmptyString();
- case 1:
- return scriptContext->GetLibrary()->GetCharStringCache().GetStringForChar(*content);
- default:
- break;
- }
- Recycler* recycler = scriptContext->GetRecycler();
- StaticType * stringTypeStatic = scriptContext->GetLibrary()->GetStringTypeStatic();
- char16 const * buffer = content;
- charcount_t cchUseBoundLength = static_cast<charcount_t>(cchUseLength);
- if (copyBuffer)
- {
- buffer = JavascriptString::AllocateLeafAndCopySz(recycler, content, cchUseBoundLength);
- }
- return T::New(stringTypeStatic, buffer, cchUseBoundLength, recycler);
- }
- JavascriptString* JavascriptString::NewWithSz(__in_z const char16 * content, ScriptContext * scriptContext)
- {
- AssertMsg(content != nullptr, "NULL value passed to JavascriptString::New");
- return NewWithBuffer(content, GetBufferLength(content), scriptContext);
- }
- JavascriptString* JavascriptString::NewWithBuffer(__in_ecount(cchUseLength) const char16 * content, charcount_t cchUseLength, ScriptContext * scriptContext)
- {
- return NewWithBufferT<LiteralString, false>(content, cchUseLength, scriptContext);
- }
- JavascriptString* JavascriptString::NewCopySz(__in_z const char16* content, ScriptContext* scriptContext)
- {
- return NewCopyBuffer(content, GetBufferLength(content), scriptContext);
- }
- JavascriptString* JavascriptString::NewCopyBuffer(__in_ecount(cchUseLength) const char16* content, charcount_t cchUseLength, ScriptContext* scriptContext)
- {
- return NewWithBufferT<LiteralString, true>(content, cchUseLength, scriptContext);
- }
- Var JavascriptString::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AssertMsg(args.Info.Count > 0, "Negative argument count");
- // SkipDefaultNewObject function flag should have prevented the default object from
- // being created, except when call true a host dispatch.
- Var newTarget = args.GetNewTarget();
- bool isCtorSuperCall = JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
- JavascriptString* str;
- Var result;
- if (args.Info.Count > 1)
- {
- JavascriptSymbol * symbol = JavascriptOperators::TryFromVar<JavascriptSymbol>(args[1]);
- if (symbol && !(callInfo.Flags & CallFlags_New))
- {
- // By ES2015 21.1.1.1 step 2, calling the String constructor directly results in an explicit ToString, which does not throw.
- return JavascriptSymbol::ToString(symbol->GetValue(), scriptContext);
- // Calling with new is an implicit ToString on the Symbol, resulting in a throw. For this case we can let JavascriptConversion handle the call.
- }
- str = JavascriptConversion::ToString(args[1], scriptContext);
- }
- else
- {
- str = scriptContext->GetLibrary()->GetEmptyString();
- }
- if (callInfo.Flags & CallFlags_New)
- {
- result = scriptContext->GetLibrary()->CreateStringObject(str);
- }
- else
- {
- result = str;
- }
- return isCtorSuperCall ?
- JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), RecyclableObject::UnsafeFromVar(result), nullptr, scriptContext) :
- result;
- }
- // static
- bool IsValidCharCount(size_t charCount)
- {
- return charCount <= JavascriptString::MaxCharLength;
- }
- JavascriptString::JavascriptString(StaticType * type)
- : RecyclableObject(type), m_charLength(0), m_pszValue(nullptr)
- {
- Assert(type->GetTypeId() == TypeIds_String);
- }
- JavascriptString::JavascriptString(StaticType * type, charcount_t charLength, const char16* szValue)
- : RecyclableObject(type), m_pszValue(szValue)
- {
- Assert(type->GetTypeId() == TypeIds_String);
- SetLength(charLength);
- }
- _Ret_range_(m_charLength, m_charLength)
- charcount_t JavascriptString::GetLength() const
- {
- return m_charLength;
- }
- int JavascriptString::GetLengthAsSignedInt() const
- {
- Assert(IsValidCharCount(m_charLength));
- return static_cast<int>(m_charLength);
- }
- const char16* JavascriptString::UnsafeGetBuffer() const
- {
- return m_pszValue;
- }
- void JavascriptString::SetLength(charcount_t newLength)
- {
- if (!IsValidCharCount(newLength))
- {
- JavascriptExceptionOperators::ThrowOutOfMemory(this->GetScriptContext());
- }
- m_charLength = newLength;
- }
- void JavascriptString::SetBuffer(const char16* buffer)
- {
- m_pszValue = buffer;
- }
- bool JavascriptString::IsValidIndexValue(charcount_t idx) const
- {
- return IsValidCharCount(idx) && idx < GetLength();
- }
- bool JavascriptString::Is(Var aValue)
- {
- return JavascriptOperators::GetTypeId(aValue) == TypeIds_String;
- }
- Js::PropertyRecord const * JavascriptString::GetPropertyRecord(bool dontLookupFromDictionary)
- {
- if (dontLookupFromDictionary)
- {
- return nullptr;
- }
- Js::PropertyRecord const * propertyRecord;
- GetScriptContext()->GetOrAddPropertyRecord(GetString(), GetLength(), &propertyRecord);
- return propertyRecord;
- }
- JavascriptString* JavascriptString::FromVar(Var aValue)
- {
- AssertOrFailFastMsg(Is(aValue), "Ensure var is actually a 'JavascriptString'");
- return static_cast<JavascriptString *>(aValue);
- }
- JavascriptString* JavascriptString::UnsafeFromVar(Var aValue)
- {
- AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptString'");
- return static_cast<JavascriptString *>(aValue);
- }
- charcount_t
- JavascriptString::GetBufferLength(const char16 * content)
- {
- size_t cchActual = wcslen(content);
- #if defined(_M_X64_OR_ARM64)
- if (!IsValidCharCount(cchActual))
- {
- // Limit javascript string to 31-bit length
- Js::Throw::OutOfMemory();
- }
- #else
- // There shouldn't be enough memory to have UINT_MAX character.
- // INT_MAX is the upper bound for 32-bit;
- Assert(IsValidCharCount(cchActual));
- #endif
- return static_cast<charcount_t>(cchActual);
- }
- charcount_t
- JavascriptString::GetBufferLength(
- const char16 * content, // Value to examine
- int charLengthOrMinusOne) // Optional length, in characters
- {
- //
- // Determine the actual length, in characters, not including a terminating '\0':
- // - If a length was not specified (charLength < 0), search for a terminating '\0'.
- //
- charcount_t cchActual;
- if (charLengthOrMinusOne < 0)
- {
- AssertMsg(charLengthOrMinusOne == -1, "The only negative value allowed is -1");
- cchActual = GetBufferLength(content);
- }
- else
- {
- cchActual = static_cast<charcount_t>(charLengthOrMinusOne);
- }
- #ifdef CHECK_STRING
- // removed this to accommodate much larger string constant in regex-dna.js
- if (cchActual > 64 * 1024)
- {
- //
- // String was probably not '\0' terminated:
- // - We need to validate that the string's contents always fit within 1 GB to avoid
- // overflow checking on 32-bit when using 'int' for 'byte *' pointer operations.
- //
- Throw::OutOfMemory(); // TODO: determine argument error
- }
- #endif
- return cchActual;
- }
- template< size_t N >
- Var JavascriptString::StringBracketHelper(Arguments args, ScriptContext *scriptContext, const char16(&tag)[N])
- {
- CompileAssert(0 < N && N <= JavascriptString::MaxCharLength);
- return StringBracketHelper(args, scriptContext, tag, static_cast<charcount_t>(N - 1), nullptr, 0);
- }
- template< size_t N1, size_t N2 >
- Var JavascriptString::StringBracketHelper(Arguments args, ScriptContext *scriptContext, const char16(&tag)[N1], const char16(&prop)[N2])
- {
- CompileAssert(0 < N1 && N1 <= JavascriptString::MaxCharLength);
- CompileAssert(0 < N2 && N2 <= JavascriptString::MaxCharLength);
- return StringBracketHelper(args, scriptContext, tag, static_cast<charcount_t>(N1 - 1), prop, static_cast<charcount_t>(N2 - 1));
- }
- BOOL JavascriptString::BufferEquals(__in_ecount(otherLength) LPCWSTR otherBuffer, __in charcount_t otherLength)
- {
- return otherLength == this->GetLength() &&
- JsUtil::CharacterBuffer<WCHAR>::StaticEquals(this->GetString(), otherBuffer, otherLength);
- }
- BOOL JavascriptString::HasItemAt(charcount_t index)
- {
- return IsValidIndexValue(index);
- }
- BOOL JavascriptString::GetItemAt(charcount_t index, Var* value)
- {
- if (!IsValidIndexValue(index))
- {
- return false;
- }
- char16 character = GetItem(index);
- *value = this->GetLibrary()->GetCharStringCache().GetStringForChar(character);
- return true;
- }
- char16 JavascriptString::GetItem(charcount_t index)
- {
- AssertMsg( IsValidIndexValue(index), "Must specify valid character");
- const char16 *str = this->GetString();
- return str[index];
- }
- void JavascriptString::CopyHelper(__out_ecount(countNeeded) char16 *dst, __in_ecount(countNeeded) const char16 * str, charcount_t countNeeded)
- {
- switch(countNeeded)
- {
- case 0:
- return;
- case 1:
- dst[0] = str[0];
- break;
- case 3:
- dst[2] = str[2];
- goto case_2;
- case 5:
- dst[4] = str[4];
- goto case_4;
- case 7:
- dst[6] = str[6];
- goto case_6;
- case 9:
- dst[8] = str[8];
- goto case_8;
- case 10:
- *(uint32 *)(dst+8) = *(uint32*)(str+8);
- // FALLTHROUGH
- case 8:
- case_8:
- *(uint32 *)(dst+6) = *(uint32*)(str+6);
- // FALLTHROUGH
- case 6:
- case_6:
- *(uint32 *)(dst+4) = *(uint32*)(str+4);
- // FALLTHROUGH
- case 4:
- case_4:
- *(uint32 *)(dst+2) = *(uint32*)(str+2);
- // FALLTHROUGH
- case 2:
- case_2:
- *(uint32 *)(dst) = *(uint32*)str;
- break;
- default:
- js_wmemcpy_s(dst, countNeeded, str, countNeeded);
- }
- }
- JavascriptString* JavascriptString::ConcatDestructive(JavascriptString* pstRight)
- {
- Assert(pstRight);
- if(!IsFinalized())
- {
- if(CompoundString::Is(this))
- {
- return ConcatDestructive_Compound(pstRight);
- }
- if(VirtualTableInfo<ConcatString>::HasVirtualTable(this))
- {
- JavascriptString *const s = ConcatDestructive_ConcatToCompound(pstRight);
- if(s)
- {
- return s;
- }
- }
- }
- else
- {
- const CharCount leftLength = GetLength();
- const CharCount rightLength = pstRight->GetLength();
- if(leftLength == 0 || rightLength == 0)
- {
- return ConcatDestructive_OneEmpty(pstRight);
- }
- if(CompoundString::ShouldAppendChars(leftLength) && CompoundString::ShouldAppendChars(rightLength))
- {
- return ConcatDestructive_CompoundAppendChars(pstRight);
- }
- }
- #ifdef PROFILE_STRINGS
- StringProfiler::RecordConcatenation(GetScriptContext(), GetLength(), pstRight->GetLength(), ConcatType_ConcatTree);
- #endif
- if(PHASE_TRACE_StringConcat)
- {
- Output::Print(
- _u("JavascriptString::ConcatDestructive(\"%.8s%s\") - creating ConcatString\n"),
- pstRight->IsFinalized() ? pstRight->GetString() : _u(""),
- !pstRight->IsFinalized() || pstRight->GetLength() > 8 ? _u("...") : _u(""));
- Output::Flush();
- }
- return ConcatString::New(this, pstRight);
- }
- JavascriptString* JavascriptString::ConcatDestructive_Compound(JavascriptString* pstRight)
- {
- Assert(CompoundString::Is(this));
- Assert(pstRight);
- #ifdef PROFILE_STRINGS
- StringProfiler::RecordConcatenation(GetScriptContext(), GetLength(), pstRight->GetLength(), ConcatType_CompoundString);
- #endif
- if(PHASE_TRACE_StringConcat)
- {
- Output::Print(
- _u("JavascriptString::ConcatDestructive(\"%.8s%s\") - appending to CompoundString\n"),
- pstRight->IsFinalized() ? pstRight->GetString() : _u(""),
- !pstRight->IsFinalized() || pstRight->GetLength() > 8 ? _u("...") : _u(""));
- Output::Flush();
- }
- CompoundString *const leftCs = CompoundString::FromVar(this);
- leftCs->PrepareForAppend();
- leftCs->Append(pstRight);
- return this;
- }
- JavascriptString* JavascriptString::ConcatDestructive_ConcatToCompound(JavascriptString* pstRight)
- {
- Assert(VirtualTableInfo<ConcatString>::HasVirtualTable(this));
- Assert(pstRight);
- const ConcatString *const leftConcatString = static_cast<const ConcatString *>(this);
- JavascriptString *const leftLeftString = leftConcatString->LeftString();
- if(VirtualTableInfo<ConcatString>::HasVirtualTable(leftLeftString))
- {
- #ifdef PROFILE_STRINGS
- StringProfiler::RecordConcatenation(GetScriptContext(), GetLength(), pstRight->GetLength(), ConcatType_CompoundString);
- #endif
- if(PHASE_TRACE_StringConcat)
- {
- Output::Print(
- _u("JavascriptString::ConcatDestructive(\"%.8s%s\") - converting ConcatString to CompoundString\n"),
- pstRight->IsFinalized() ? pstRight->GetString() : _u(""),
- !pstRight->IsFinalized() || pstRight->GetLength() > 8 ? _u("...") : _u(""));
- Output::Flush();
- }
- const ConcatString *const leftLeftConcatString = static_cast<const ConcatString *>(leftConcatString->LeftString());
- CompoundString *const cs = CompoundString::NewWithPointerCapacity(8, GetLibrary());
- cs->Append(leftLeftConcatString->LeftString());
- cs->Append(leftLeftConcatString->RightString());
- cs->Append(leftConcatString->RightString());
- cs->Append(pstRight);
- return cs;
- }
- return nullptr;
- }
- JavascriptString* JavascriptString::ConcatDestructive_OneEmpty(JavascriptString* pstRight)
- {
- Assert(pstRight);
- Assert(GetLength() == 0 || pstRight->GetLength() == 0);
- #ifdef PROFILE_STRINGS
- StringProfiler::RecordConcatenation(GetScriptContext(), GetLength(), pstRight->GetLength());
- #endif
- if(PHASE_TRACE_StringConcat)
- {
- Output::Print(
- _u("JavascriptString::ConcatDestructive(\"%.8s%s\") - one side empty, using other side\n"),
- pstRight->IsFinalized() ? pstRight->GetString() : _u(""),
- !pstRight->IsFinalized() || pstRight->GetLength() > 8 ? _u("...") : _u(""));
- Output::Flush();
- }
- if(GetLength() == 0)
- {
- return CompoundString::GetImmutableOrScriptUnreferencedString(pstRight);
- }
- Assert(CompoundString::GetImmutableOrScriptUnreferencedString(this) == this);
- return this;
- }
- JavascriptString* JavascriptString::ConcatDestructive_CompoundAppendChars(JavascriptString* pstRight)
- {
- Assert(pstRight);
- Assert(
- GetLength() != 0 &&
- pstRight->GetLength() != 0 &&
- (CompoundString::ShouldAppendChars(GetLength()) || CompoundString::ShouldAppendChars(pstRight->GetLength())));
- #ifdef PROFILE_STRINGS
- StringProfiler::RecordConcatenation(GetScriptContext(), GetLength(), pstRight->GetLength(), ConcatType_CompoundString);
- #endif
- if(PHASE_TRACE_StringConcat)
- {
- Output::Print(
- _u("JavascriptString::ConcatDestructive(\"%.8s%s\") - creating CompoundString, appending chars\n"),
- pstRight->IsFinalized() ? pstRight->GetString() : _u(""),
- !pstRight->IsFinalized() || pstRight->GetLength() > 8 ? _u("...") : _u(""));
- Output::Flush();
- }
- CompoundString *const cs = CompoundString::NewWithPointerCapacity(4, GetLibrary());
- cs->AppendChars(this);
- cs->AppendChars(pstRight);
- return cs;
- }
- JavascriptString* JavascriptString::Concat(JavascriptString* pstLeft, JavascriptString* pstRight)
- {
- AssertMsg(pstLeft != nullptr, "Must have a valid left string");
- AssertMsg(pstRight != nullptr, "Must have a valid right string");
- if(!pstLeft->IsFinalized())
- {
- if(CompoundString::Is(pstLeft))
- {
- return Concat_Compound(pstLeft, pstRight);
- }
- if(VirtualTableInfo<ConcatString>::HasVirtualTable(pstLeft))
- {
- return Concat_ConcatToCompound(pstLeft, pstRight);
- }
- }
- else if(pstLeft->GetLength() == 0 || pstRight->GetLength() == 0)
- {
- return Concat_OneEmpty(pstLeft, pstRight);
- }
- if(pstLeft->GetLength() != 1 || pstRight->GetLength() != 1)
- {
- #ifdef PROFILE_STRINGS
- StringProfiler::RecordConcatenation(pstLeft->GetScriptContext(), pstLeft->GetLength(), pstRight->GetLength(), ConcatType_ConcatTree);
- #endif
- if(PHASE_TRACE_StringConcat)
- {
- Output::Print(
- _u("JavascriptString::Concat(\"%.8s%s\") - creating ConcatString\n"),
- pstRight->IsFinalized() ? pstRight->GetString() : _u(""),
- !pstRight->IsFinalized() || pstRight->GetLength() > 8 ? _u("...") : _u(""));
- Output::Flush();
- }
- return ConcatString::New(pstLeft, pstRight);
- }
- return Concat_BothOneChar(pstLeft, pstRight);
- }
- JavascriptString* JavascriptString::Concat_Compound(JavascriptString * pstLeft, JavascriptString * pstRight)
- {
- Assert(pstLeft);
- Assert(CompoundString::Is(pstLeft));
- Assert(pstRight);
- #ifdef PROFILE_STRINGS
- StringProfiler::RecordConcatenation(pstLeft->GetScriptContext(), pstLeft->GetLength(), pstRight->GetLength(), ConcatType_CompoundString);
- #endif
- if(PHASE_TRACE_StringConcat)
- {
- Output::Print(
- _u("JavascriptString::Concat(\"%.8s%s\") - cloning CompoundString, appending to clone\n"),
- pstRight->IsFinalized() ? pstRight->GetString() : _u(""),
- !pstRight->IsFinalized() || pstRight->GetLength() > 8 ? _u("...") : _u(""));
- Output::Flush();
- }
- // This is not a left-dead concat, but we can reuse available space in the left string
- // because it may be accessible by script code, append to a clone.
- const bool needAppend = pstRight->GetLength() != 0;
- CompoundString *const leftCs = CompoundString::FromVar(pstLeft)->Clone(needAppend);
- if(needAppend)
- {
- leftCs->Append(pstRight);
- }
- return leftCs;
- }
- JavascriptString* JavascriptString::Concat_ConcatToCompound(JavascriptString * pstLeft, JavascriptString * pstRight)
- {
- Assert(pstLeft);
- Assert(VirtualTableInfo<ConcatString>::HasVirtualTable(pstLeft));
- Assert(pstRight);
- #ifdef PROFILE_STRINGS
- StringProfiler::RecordConcatenation(pstLeft->GetScriptContext(), pstLeft->GetLength(), pstRight->GetLength(), ConcatType_CompoundString);
- #endif
- if(PHASE_TRACE_StringConcat)
- {
- Output::Print(
- _u("JavascriptString::Concat(\"%.8s%s\") - converting ConcatString to CompoundString\n"),
- pstRight->IsFinalized() ? pstRight->GetString() : _u(""),
- !pstRight->IsFinalized() || pstRight->GetLength() > 8 ? _u("...") : _u(""));
- Output::Flush();
- }
- const ConcatString *const leftConcatString = static_cast<const ConcatString *>(pstLeft);
- CompoundString *const cs = CompoundString::NewWithPointerCapacity(8, pstLeft->GetLibrary());
- cs->Append(leftConcatString->LeftString());
- cs->Append(leftConcatString->RightString());
- cs->Append(pstRight);
- return cs;
- }
- JavascriptString* JavascriptString::Concat_OneEmpty(JavascriptString * pstLeft, JavascriptString * pstRight)
- {
- Assert(pstLeft);
- Assert(pstRight);
- Assert(pstLeft->GetLength() == 0 || pstRight->GetLength() == 0);
- #ifdef PROFILE_STRINGS
- StringProfiler::RecordConcatenation(pstLeft->GetScriptContext(), pstLeft->GetLength(), pstRight->GetLength());
- #endif
- if(PHASE_TRACE_StringConcat)
- {
- Output::Print(
- _u("JavascriptString::Concat(\"%.8s%s\") - one side empty, using other side\n"),
- pstRight->IsFinalized() ? pstRight->GetString() : _u(""),
- !pstRight->IsFinalized() || pstRight->GetLength() > 8 ? _u("...") : _u(""));
- Output::Flush();
- }
- if(pstLeft->GetLength() == 0)
- {
- return CompoundString::GetImmutableOrScriptUnreferencedString(pstRight);
- }
- Assert(CompoundString::GetImmutableOrScriptUnreferencedString(pstLeft) == pstLeft);
- return pstLeft;
- }
- JavascriptString* JavascriptString::Concat_BothOneChar(JavascriptString * pstLeft, JavascriptString * pstRight)
- {
- Assert(pstLeft);
- Assert(pstLeft->GetLength() == 1);
- Assert(pstRight);
- Assert(pstRight->GetLength() == 1);
- #ifdef PROFILE_STRINGS
- StringProfiler::RecordConcatenation(pstLeft->GetScriptContext(), pstLeft->GetLength(), pstRight->GetLength(), ConcatType_BufferString);
- #endif
- if(PHASE_TRACE_StringConcat)
- {
- Output::Print(
- _u("JavascriptString::Concat(\"%.8s%s\") - both sides length 1, creating BufferStringBuilder::WritableString\n"),
- pstRight->IsFinalized() ? pstRight->GetString() : _u(""),
- !pstRight->IsFinalized() || pstRight->GetLength() > 8 ? _u("...") : _u(""));
- Output::Flush();
- }
- ScriptContext* scriptContext = pstLeft->GetScriptContext();
- BufferStringBuilder builder(2, scriptContext);
- char16 * stringBuffer = builder.DangerousGetWritableBuffer();
- stringBuffer[0] = *pstLeft->GetString();
- stringBuffer[1] = *pstRight->GetString();
- return builder.ToString();
- }
- Var JavascriptString::EntryCharAt(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- //
- // General algorithm:
- // 1. Call CheckObjectCoercible passing the this value as its argument.
- // 2. Let S be the result of calling ToString, giving it the this value as its argument.
- // 3. Let position be ToInteger(pos).
- // 4. Let size be the number of characters in S.
- // 5. If position < 0 or position = size, return the empty string.
- // 6. Return a string of length 1, containing one character from S, where the first (leftmost) character in S is considered to be at position 0, the next one at position 1, and so on.
- // NOTE
- // The charAt function is intentionally generic; it does not require that its this value be a String object. Therefore, it can be transferred to other kinds of objects for use as a method.
- //
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.charAt"), &pThis);
- charcount_t idxPosition = 0;
- if (args.Info.Count > 1)
- {
- idxPosition = ConvertToIndex(args[1], scriptContext);
- }
- //
- // Get the character at the specified position.
- //
- Var value;
- if (pThis->GetItemAt(idxPosition, &value))
- {
- return value;
- }
- else
- {
- return scriptContext->GetLibrary()->GetEmptyString();
- }
- }
- Var JavascriptString::EntryCharCodeAt(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- //
- // General algorithm:
- // 1. Call CheckObjectCoercible passing the this value as its argument.
- // 2. Let S be the result of calling ToString, giving it the this value as its argument.
- // 3. Let position be ToInteger(pos).
- // 4. Let size be the number of characters in S.
- // 5. If position < 0 or position = size, return NaN.
- // 6. Return a value of Number type, whose value is the code unit value of the character at that position in the string S, where the first (leftmost) character in S is considered to be at position 0, the next one at position 1, and so on.
- // NOTE
- // The charCodeAt function is intentionally generic; it does not require that its this value be a String object. Therefore it can be transferred to other kinds of objects for use as a method.
- //
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.charCodeAt"), &pThis);
- charcount_t idxPosition = 0;
- if (args.Info.Count > 1)
- {
- idxPosition = ConvertToIndex(args[1], scriptContext);
- }
- //
- // Get the character at the specified position.
- //
- charcount_t charLength = pThis->GetLength();
- if (idxPosition >= charLength)
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- return TaggedInt::ToVarUnchecked(pThis->GetItem(idxPosition));
- }
- Var JavascriptString::EntryCodePointAt(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.codePointAt"), &pThis);
- charcount_t idxPosition = 0;
- if (args.Info.Count > 1)
- {
- idxPosition = ConvertToIndex(args[1], scriptContext);
- }
- charcount_t charLength = pThis->GetLength();
- if (idxPosition >= charLength)
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- // A surrogate pair consists of two characters, a lower part and a higher part.
- // Lower part is in range [0xD800 - 0xDBFF], while the higher is [0xDC00 - 0xDFFF].
- char16 first = pThis->GetItem(idxPosition);
- if (first >= 0xD800u && first < 0xDC00u && (uint)(idxPosition + 1) < pThis->GetLength())
- {
- char16 second = pThis->GetItem(idxPosition + 1);
- if (second >= 0xDC00 && second < 0xE000)
- {
- return TaggedInt::ToVarUnchecked(NumberUtilities::SurrogatePairAsCodePoint(first, second));
- }
- }
- return TaggedInt::ToVarUnchecked(first);
- }
- Var JavascriptString::EntryConcat(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- //
- // General algorithm:
- // 1. Call CheckObjectCoercible passing the this value as its argument.
- // 2. Let S be the result of calling ToString, giving it the this value as its argument.
- // 3. Let args be an internal list that is a copy of the argument list passed to this function.
- // 4. Let R be S.
- // 5. Repeat, while args is not empty
- // Remove the first element from args and let next be the value of that element.
- // Let R be the string value consisting of the characters in the previous value of R followed by the characters of ToString(next).
- // 6. Return R.
- //
- // NOTE
- // The concat function is intentionally generic; it does not require that its this value be a String object. Therefore it can be transferred to other kinds of objects for use as a method.
- //
- if(args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedString, _u("String.prototype.concat"));
- }
- AssertMsg(args.Info.Count > 0, "Negative argument count");
- if (!JavascriptConversion::CheckObjectCoercible(args[0], scriptContext))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("String.prototype.concat"));
- }
- JavascriptString* accum = nullptr;
- for (uint index = 0; index < args.Info.Count; index++)
- {
- JavascriptString * pstr = JavascriptOperators::TryFromVar<JavascriptString>(args[index]);
- if (!pstr)
- {
- pstr = JavascriptConversion::ToString(args[index], scriptContext);
- }
- if (index == 0)
- {
- accum = pstr;
- }
- else
- {
- accum = Concat(accum,pstr);
- }
- }
- return accum;
- }
- Var JavascriptString::EntryFromCharCode(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- //
- // Construct a new string instance to contain all of the explicit parameters:
- // - Don't include the 'this' parameter.
- //
- if(args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedString, _u("String.fromCharCode"));
- }
- AssertMsg(args.Info.Count > 0, "Negative argument count");
- int charLength = args.Info.Count - 1;
- // Special case for single char
- if( charLength == 1 )
- {
- char16 ch = JavascriptConversion::ToUInt16(args[1], scriptContext);
- return scriptContext->GetLibrary()->GetCharStringCache().GetStringForChar(ch);
- }
- BufferStringBuilder builder(charLength,scriptContext);
- char16 * stringBuffer = builder.DangerousGetWritableBuffer();
- //
- // Call ToUInt16 for each parameter, storing the character at the appropriate position.
- //
- for (uint idxArg = 1; idxArg < args.Info.Count; idxArg++)
- {
- *stringBuffer++ = JavascriptConversion::ToUInt16(args[idxArg], scriptContext);
- }
- //
- // Return the new string instance.
- //
- return builder.ToString();
- }
- Var JavascriptString::EntryFromCodePoint(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- AssertMsg(args.Info.Count > 0, "Negative argument count");
- if (args.Info.Count <= 1)
- {
- return scriptContext->GetLibrary()->GetEmptyString();
- }
- else if (args.Info.Count == 2)
- {
- // Special case for a single char string formed from only code point in range [0x0, 0xFFFF]
- double num = JavascriptConversion::ToNumber(args[1], scriptContext);
- if (!NumberUtilities::IsFinite(num))
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_InvalidCodePoint);
- }
- if (num < 0 || num > 0x10FFFF || floor(num) != num)
- {
- JavascriptError::ThrowRangeErrorVar(scriptContext, JSERR_InvalidCodePoint, Js::JavascriptConversion::ToString(args[1], scriptContext)->GetSz());
- }
- if (num < 0x10000)
- {
- return scriptContext->GetLibrary()->GetCharStringCache().GetStringForChar((uint16)num);
- }
- }
- BEGIN_TEMP_ALLOCATOR(tempAllocator, scriptContext, _u("fromCodePoint"));
- // Create a temporary buffer that is double the arguments count (in case all are surrogate pairs)
- size_t bufferLength = (args.Info.Count - 1) * 2;
- char16 *tempBuffer = AnewArray(tempAllocator, char16, bufferLength);
- uint32 count = 0;
- for (uint i = 1; i < args.Info.Count; i++)
- {
- double num = JavascriptConversion::ToNumber(args[i], scriptContext);
- if (!NumberUtilities::IsFinite(num))
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_InvalidCodePoint);
- }
- if (num < 0 || num > 0x10FFFF || floor(num) != num)
- {
- JavascriptError::ThrowRangeErrorVar(scriptContext, JSERR_InvalidCodePoint, Js::JavascriptConversion::ToString(args[i], scriptContext)->GetSz());
- }
- if (num < 0x10000)
- {
- __analysis_assume(count < bufferLength);
- Assert(count < bufferLength);
- #pragma prefast(suppress: 22102, "I have an assert in place to guard against overflow. Even though this should never happen.")
- tempBuffer[count] = (char16)num;
- count++;
- }
- else
- {
- __analysis_assume(count + 1 < bufferLength);
- Assert(count + 1 < bufferLength);
- NumberUtilities::CodePointAsSurrogatePair((codepoint_t)num, (tempBuffer + count), (tempBuffer + count + 1));
- count += 2;
- }
- }
- // Create a string of appropriate length
- __analysis_assume(count <= bufferLength);
- Assert(count <= bufferLength);
- JavascriptString *toReturn = JavascriptString::NewCopyBuffer(tempBuffer, count, scriptContext);
- END_TEMP_ALLOCATOR(tempAllocator, scriptContext);
- return toReturn;
- }
- Var JavascriptString::EntryIndexOf(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- return JavascriptNumber::ToVar(IndexOf(args, scriptContext, _u("String.prototype.indexOf"), true), scriptContext);
- }
- int JavascriptString::IndexOf(ArgumentReader& args, ScriptContext* scriptContext, const char16* apiNameForErrorMsg, bool isRegExpAnAllowedArg)
- {
- // The algorithm steps in the spec are the same between String.prototype.indexOf and
- // String.prototype.includes, except that includes returns true if an index is found,
- // false otherwise. Share the implementation between these two APIs.
- //
- // 1. Call CheckObjectCoercible passing the this value as its argument.
- // 2. Let S be the result of calling ToString, giving it the this value as its argument.
- // 3. Let searchStr be ToString(searchString).
- // 4. Let pos be ToInteger(position). (If position is undefined, this step produces the value 0).
- // 5. Let len be the number of characters in S.
- // 6. Let start be min(max(pos, 0), len).
- // 7. Let searchLen be the number of characters in searchStr.
- // 8. Return the smallest possible integer k not smaller than start such that k+ searchLen is not greater than len, and for all nonnegative integers j less than searchLen, the character at position k+j of S is the same as the character at position j of searchStr); but if there is no such integer k, then return the value -1.
- // NOTE
- // The indexOf function is intentionally generic; it does not require that its this value be a String object. Therefore, it can be transferred to other kinds of objects for use as a method.
- //
- JavascriptString * pThis;
- JavascriptString * searchString;
- GetThisAndSearchStringArguments(args, scriptContext, apiNameForErrorMsg, &pThis, &searchString, isRegExpAnAllowedArg);
- int len = pThis->GetLength();
- int searchLen = searchString->GetLength();
- int position = 0;
- if (args.Info.Count > 2)
- {
- if (JavascriptOperators::IsUndefinedObject(args[2], scriptContext))
- {
- position = 0;
- }
- else
- {
- position = ConvertToIndex(args[2], scriptContext); // this is to adjust corner cases like MAX_VALUE
- position = min(max(position, 0), len); // adjust position within string limits
- }
- }
- // Zero length search strings are always found at the current search position
- if (searchLen == 0)
- {
- return position;
- }
- int result = -1;
- if (position < pThis->GetLengthAsSignedInt())
- {
- const char16* searchStr = searchString->GetString();
- const char16* inputStr = pThis->GetString();
- if (searchLen == 1)
- {
- int i = position;
- for(; i < len && inputStr[i] != *searchStr ; i++);
- if (i < len)
- {
- result = i;
- }
- }
- else
- {
- JmpTable jmpTable;
- bool fAsciiJumpTable = BuildLastCharForwardBoyerMooreTable(jmpTable, searchStr, searchLen);
- if (!fAsciiJumpTable)
- {
- result = JavascriptString::strstr(pThis, searchString, false, position);
- }
- else
- {
- result = IndexOfUsingJmpTable(jmpTable, inputStr, len, searchStr, searchLen, position);
- }
- }
- }
- return result;
- }
- Var JavascriptString::EntryLastIndexOf(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- // ES #sec-string.prototype.lastindexof
- // 21.1.3.9 String.prototype.lastIndexOf(searchString[, position])
- //
- // 1. Let O be ? RequireObjectCoercible(this value).
- // 2. Let S be ? ToString(O).
- // 3. Let searchStr be ? ToString(searchString).
- // default search string if the search argument is not provided
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.lastIndexOf"), &pThis);
- JavascriptString * searchArg = nullptr;
- if(args.Info.Count > 1)
- {
- searchArg = JavascriptOperators::TryFromVar<JavascriptString>(args[1]);
- if (!searchArg)
- {
- searchArg = JavascriptConversion::ToString(args[1], scriptContext);
- }
- }
- else
- {
- searchArg = scriptContext->GetLibrary()->GetUndefinedDisplayString();
- }
- const char16* const inputStr = pThis->GetString();
- const char16 * const searchStr = searchArg->GetString();
- // 4. Let numPos be ? ToNumber(position). (If position is undefined, this step produces the value NaN.)
- // 5. If numPos is NaN, let pos be +infinity; otherwise, let pos be ToInteger(numPos).
- // 6. Let len be the number of elements in S.
- // 7. Let start be min(max(pos, 0), len).
- const charcount_t inputLen = pThis->GetLength();
- const charcount_t searchLen = searchArg->GetLength();
- charcount_t position = inputLen;
- const char16* const searchLowerBound = inputStr;
- // Determine if the main string can't contain the search string by length
- if (searchLen > inputLen)
- {
- return JavascriptNumber::ToVar(-1, scriptContext);
- }
- if (args.Info.Count > 2)
- {
- double pos = JavascriptConversion::ToNumber(args[2], scriptContext);
- if (!JavascriptNumber::IsNan(pos))
- {
- pos = JavascriptConversion::ToInteger(pos);
- if (pos > inputLen - searchLen)
- {
- // No point searching beyond the possible end point.
- pos = inputLen - searchLen;
- }
- position = (charcount_t)min(max(pos, (double)0), (double)inputLen); // adjust position within string limits
- }
- }
- if (position > inputLen - searchLen)
- {
- // No point searching beyond the possible end point.
- position = inputLen - searchLen;
- }
- const char16* const searchUpperBound = searchLowerBound + min(position, inputLen - 1);
- // 8. Let searchLen be the number of elements in searchStr.
- // 9. Return the largest possible nonnegative integer k not larger than start such that k + searchLen is
- // not greater than len, and for all nonnegative integers j less than searchLen, the code unit at
- // index k + j of S is the same as the code unit at index j of searchStr; but if there is no such
- // integer k, return the value - 1.
- // Note: The lastIndexOf function is intentionally generic; it does not require that its this value be a
- // String object. Therefore, it can be transferred to other kinds of objects for use as a method.
- // Zero length search strings are always found at the current search position
- if (searchLen == 0)
- {
- return JavascriptNumber::ToVar(position, scriptContext);
- }
- else if (searchLen == 1)
- {
- char16 const * current = searchUpperBound;
- while (*current != *searchStr)
- {
- current--;
- if (current < inputStr)
- {
- return JavascriptNumber::ToVar(-1, scriptContext);
- }
- }
- return JavascriptNumber::ToVar(current - inputStr, scriptContext);
- }
- // Structure for a partial ASCII Boyer-Moore
- JmpTable jmpTable;
- if (BuildFirstCharBackwardBoyerMooreTable(jmpTable, searchStr, searchLen))
- {
- int result = LastIndexOfUsingJmpTable(jmpTable, inputStr, inputLen, searchStr, searchLen, position);
- return JavascriptNumber::ToVar(result, scriptContext);
- }
- // Revert to slow search if we decided not to do Boyer-Moore.
- char16 const * currentPos = searchUpperBound;
- Assert(currentPos - searchLowerBound + searchLen <= inputLen);
- while (currentPos >= searchLowerBound)
- {
- if (*currentPos == *searchStr)
- {
- // Quick start char chec
- if (wmemcmp(currentPos, searchStr, searchLen) == 0)
- {
- return JavascriptNumber::ToVar(currentPos - searchLowerBound, scriptContext);
- }
- }
- --currentPos;
- }
- return JavascriptNumber::ToVar(-1, scriptContext);
- }
- // Performs common ES spec steps for getting this argument in string form:
- // 1. Let O be CHeckObjectCoercible(this value).
- // 2. Let S be ToString(O).
- // 3. ReturnIfAbrupt(S).
- void JavascriptString::GetThisStringArgument(ArgumentReader& args, ScriptContext* scriptContext, const char16* apiNameForErrorMsg, JavascriptString** ppThis)
- {
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedString, apiNameForErrorMsg);
- }
- AssertMsg(args.Info.Count > 0, "Negative argument count");
- JavascriptString * pThis = JavascriptOperators::TryFromVar<JavascriptString>(args[0]);
- if (!pThis)
- {
- pThis = JavascriptConversion::CoerseString(args[0], scriptContext , apiNameForErrorMsg);
- }
- *ppThis = pThis;
- }
- // Performs common ES spec steps for getting this and first parameter arguments in string form:
- // 1. Let O be CHeckObjectCoercible(this value).
- // 2. Let S be ToString(O).
- // 3. ReturnIfAbrupt(S).
- // 4. Let otherStr be ToString(firstArg).
- // 5. ReturnIfAbrupt(otherStr).
- void JavascriptString::GetThisAndSearchStringArguments(ArgumentReader& args, ScriptContext* scriptContext, const char16* apiNameForErrorMsg, JavascriptString** ppThis, JavascriptString** ppSearch, bool isRegExpAnAllowedArg)
- {
- GetThisStringArgument(args, scriptContext, apiNameForErrorMsg, ppThis);
- JavascriptString * pSearch = scriptContext->GetLibrary()->GetUndefinedDisplayString();
- if (args.Info.Count > 1)
- {
- if (!isRegExpAnAllowedArg && JavascriptRegExp::IsRegExpLike(args[1], scriptContext))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_FirstCannotBeRegExp, apiNameForErrorMsg);
- }
- else
- {
- pSearch = JavascriptOperators::TryFromVar<JavascriptString>(args[1]);
- if (!pSearch)
- {
- pSearch = JavascriptConversion::ToString(args[1], scriptContext);
- }
- }
- }
- *ppSearch = pSearch;
- }
- Var JavascriptString::EntryLocaleCompare(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if(args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedString, _u("String.prototype.localeCompare"));
- }
- AssertMsg(args.Info.Count > 0, "Negative argument count");
- JavascriptString * pThis;
- JavascriptString * pThat;
- GetThisAndSearchStringArguments(args, scriptContext, _u("String.prototype.localeCompare"), &pThis, &pThat, true);
- #ifdef ENABLE_INTL_OBJECT
- if (CONFIG_FLAG(IntlBuiltIns) && scriptContext->IsIntlEnabled())
- {
- EngineInterfaceObject* nativeEngineInterfaceObj = scriptContext->GetLibrary()->GetEngineInterfaceObject();
- if (nativeEngineInterfaceObj)
- {
- IntlEngineInterfaceExtensionObject* intlExtensionObject = static_cast<IntlEngineInterfaceExtensionObject*>(
- nativeEngineInterfaceObj->GetEngineExtension(EngineInterfaceExtensionKind_Intl));
- if (args.Info.Count == 2)
- {
- auto undefined = scriptContext->GetLibrary()->GetUndefined();
- CallInfo toPass(callInfo.Flags, 7);
- ThreadContext *threadContext = scriptContext->GetThreadContext();
- return threadContext->ExecuteImplicitCall(function, ImplicitCall_Accessor,
- [threadContext, intlExtensionObject, function, toPass, undefined, pThis, pThat]() -> Var
- {
- return CALL_ENTRYPOINT(threadContext, intlExtensionObject->EntryIntl_CompareString,
- function, toPass, undefined, pThis, pThat, undefined, undefined, undefined, undefined);
- }
- );
- }
- else
- {
- JavascriptFunction* func = intlExtensionObject->GetStringLocaleCompare();
- if (func)
- {
- return func->CallFunction(args);
- }
- // Initialize String.prototype.toLocaleCompare
- scriptContext->GetLibrary()->InitializeIntlForStringPrototype();
- func = intlExtensionObject->GetStringLocaleCompare();
- if (func)
- {
- return func->CallFunction(args);
- }
- }
- }
- }
- #endif
- const char16* pThisStr = pThis->GetString();
- int thisStrCount = pThis->GetLength();
- const char16* pThatStr = pThat->GetString();
- int thatStrCount = pThat->GetLength();
- // xplat-todo: doing a locale-insensitive compare here
- // but need to move locale-specific string comparison to
- // platform agnostic interface
- #ifdef ENABLE_GLOBALIZATION
- LCID lcid = GetUserDefaultLCID();
- int result = CompareStringW(lcid, NULL, pThisStr, thisStrCount, pThatStr, thatStrCount );
- if (result == 0)
- {
- // TODO there is no spec on the error thrown here.
- // When the support for HR errors is implemented replace this with the same error reported by v5.8
- JavascriptError::ThrowRangeError(function->GetScriptContext(),
- VBSERR_InternalError /* TODO-ERROR: _u("Failed compare operation")*/ );
- }
- return JavascriptNumber::ToVar(result-2, scriptContext);
- #else // !ENABLE_GLOBALIZATION
- // no ICU / or external support for localization. Use c-lib
- const int result = wcscmp(pThisStr, pThatStr);
- return JavascriptNumber::ToVar(result > 0 ? 1 : result == 0 ? 0 : -1, scriptContext);
- #endif
- }
- Var JavascriptString::EntryMatch(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- PCWSTR const varName = _u("String.prototype.match");
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, varName);
- auto fallback = [&](JavascriptString* stringObj)
- {
- Var regExp = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
- if (!scriptContext->GetConfig()->IsES6RegExSymbolsEnabled())
- {
- JavascriptRegExp * regExObj = JavascriptRegExp::CreateRegEx(regExp, nullptr, scriptContext);
- return RegexHelper::RegexMatch(
- scriptContext,
- regExObj,
- stringObj,
- RegexHelper::IsResultNotUsed(callInfo.Flags));
- }
- else
- {
- JavascriptRegExp * regExObj = JavascriptRegExp::CreateRegExNoCoerce(regExp, nullptr, scriptContext);
- Var symbolFn = GetRegExSymbolFunction(regExObj, PropertyIds::_symbolMatch, scriptContext);
- return CallRegExSymbolFunction<1>(symbolFn, regExObj, args, varName, scriptContext);
- }
- };
- return DelegateToRegExSymbolFunction<1>(args, PropertyIds::_symbolMatch, fallback, varName, scriptContext);
- }
- Var JavascriptString::EntryNormalize(RecyclableObject* function, CallInfo callInfo, ...)
- {
- using namespace PlatformAgnostic;
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- JavascriptString *pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.normalize"), &pThis);
- UnicodeText::NormalizationForm form = UnicodeText::NormalizationForm::C;
- if (args.Info.Count >= 2 && !(JavascriptOperators::IsUndefinedObject(args.Values[1])))
- {
- JavascriptString *formStr = JavascriptOperators::TryFromVar<JavascriptString>(args[1]);
- if (!formStr)
- {
- formStr = JavascriptConversion::ToString(args[1], scriptContext);
- }
- if (formStr->BufferEquals(_u("NFD"), 3))
- {
- form = UnicodeText::NormalizationForm::D;
- }
- else if (formStr->BufferEquals(_u("NFKC"), 4))
- {
- form = UnicodeText::NormalizationForm::KC;
- }
- else if (formStr->BufferEquals(_u("NFKD"), 4))
- {
- form = UnicodeText::NormalizationForm::KD;
- }
- else if (!formStr->BufferEquals(_u("NFC"), 3))
- {
- JavascriptError::ThrowRangeErrorVar(scriptContext, JSERR_InvalidNormalizationForm, formStr->GetString());
- }
- }
- if (UnicodeText::IsNormalizedString(form, pThis->GetSz(), pThis->GetLength()))
- {
- return pThis;
- }
- BEGIN_TEMP_ALLOCATOR(tempAllocator, scriptContext, _u("normalize"));
- charcount_t sizeEstimate = 0;
- char16* buffer = pThis->GetNormalizedString(form, tempAllocator, sizeEstimate);
- JavascriptString * retVal;
- if (buffer == nullptr)
- {
- Assert(sizeEstimate == 0);
- retVal = scriptContext->GetLibrary()->GetEmptyString();
- }
- else
- {
- retVal = JavascriptString::NewCopyBuffer(buffer, sizeEstimate, scriptContext);
- }
- END_TEMP_ALLOCATOR(tempAllocator, scriptContext);
- return retVal;
- }
- ///----------------------------------------------------------------------------
- /// String.raw(), as described in (ES6.0 (Draft 18): S21.1.2.4).
- ///----------------------------------------------------------------------------
- Var JavascriptString::EntryRaw(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count < 2)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("String.raw"));
- }
- RecyclableObject* callSite;
- RecyclableObject* raw;
- Var rawVar;
- // Call ToObject on the first argument to get the callSite (which is also cooked string array)
- // ToObject returns false if the parameter is null or undefined
- if (!JavascriptConversion::ToObject(args[1], scriptContext, &callSite))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("String.raw"));
- }
- // Get the raw property from the callSite object
- if (!callSite->GetProperty(callSite, Js::PropertyIds::raw, &rawVar, nullptr, scriptContext))
- {
- rawVar = scriptContext->GetLibrary()->GetUndefined();
- }
- if (!JavascriptConversion::ToObject(rawVar, scriptContext, &raw))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("String.raw"));
- }
- int64 length = JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(raw, scriptContext), scriptContext);
- // If there are no raw strings (somehow), return empty string
- if (length <= 0)
- {
- return scriptContext->GetLibrary()->GetEmptyString();
- }
- // Get the first raw string
- Var var = JavascriptOperators::OP_GetElementI_UInt32(raw, 0, scriptContext);
- JavascriptString* string = JavascriptConversion::ToString(var, scriptContext);
- // If there is only one raw string, just return that one raw string (doesn't matter if there are replacements)
- if (length == 1)
- {
- return string;
- }
- // We aren't going to bail early so let's create a StringBuilder and put the first raw string in there
- CompoundString::Builder<64 * sizeof(void *) / sizeof(char16)> stringBuilder(scriptContext);
- stringBuilder.Append(string);
- // Each raw string is followed by a substitution expression except for the last one
- // We will always have one more string constant than substitution expression
- // `strcon1 ${expr1} strcon2 ${expr2} strcon3` = strcon1 + expr1 + strcon2 + expr2 + strcon3
- //
- // strcon1 --- step 1 (above)
- // expr1 \__ step 2
- // strcon2 /
- // expr2 \__ step 3
- // strcon3 /
- const auto append = [&] (Var var)
- {
- JavascriptString* string = JavascriptConversion::ToString(var, scriptContext);
- stringBuilder.Append(string);
- };
- uint32 loopMax = length >= UINT_MAX ? UINT_MAX-1 : (uint32)length;
- uint32 i = 1;
- uint32 argsCount = args.Info.Count;
- for (; i < loopMax; ++i)
- {
- // First append the next substitution expression if available
- if (i + 1 < argsCount)
- {
- append(args[i + 1]);
- }
- // Then append the next string (this will also cover the final string case)
- append(JavascriptOperators::OP_GetElementI_UInt32(raw, i, scriptContext));
- }
- // Length can be greater than uint32 max (unlikely in practice)
- for (int64 j = (int64)i; j < length; ++j)
- {
- // Append whatever is left in the array/object
- append(JavascriptOperators::OP_GetElementI(raw, JavascriptNumber::ToVar(j, scriptContext), scriptContext));
- }
- // CompoundString::Builder has saved our lives
- return stringBuilder.ToString();
- }
- Var JavascriptString::EntryReplace(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- PCWSTR const varName = _u("String.prototype.replace");
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, varName);
- Assert(!(callInfo.Flags & CallFlags_New));
- auto fallback = [&](JavascriptString* stringObj)
- {
- return DoStringReplace(args, callInfo, stringObj, scriptContext);
- };
- return DelegateToRegExSymbolFunction<2>(args, PropertyIds::_symbolReplace, fallback, varName, scriptContext);
- }
- Var JavascriptString::DoStringReplace(Arguments& args, CallInfo& callInfo, JavascriptString* input, ScriptContext* scriptContext)
- {
- //
- // TODO: Move argument processing into DirectCall with proper handling.
- //
- JavascriptRegExp * pRegEx = nullptr;
- JavascriptString * pMatch = nullptr;
- JavascriptString * pReplace = nullptr;
- JavascriptFunction* replacefn = nullptr;
- SearchValueHelper(scriptContext, ((args.Info.Count > 1)?args[1]:scriptContext->GetLibrary()->GetNull()), &pRegEx, &pMatch);
- ReplaceValueHelper(scriptContext, ((args.Info.Count > 2) ? args[2] : scriptContext->GetLibrary()->GetUndefined()), &replacefn, &pReplace);
- if (pRegEx != nullptr)
- {
- if (replacefn != nullptr)
- {
- return RegexHelper::RegexReplaceFunction(scriptContext, pRegEx, input, replacefn);
- }
- else
- {
- return RegexHelper::RegexReplace(scriptContext, pRegEx, input, pReplace, RegexHelper::IsResultNotUsed(callInfo.Flags));
- }
- }
- AssertMsg(pMatch != nullptr, "Match string shouldn't be null");
- if (replacefn != nullptr)
- {
- return RegexHelper::StringReplace(scriptContext, pMatch, input, replacefn);
- }
- else
- {
- if (callInfo.Flags & CallFlags_NotUsed)
- {
- return scriptContext->GetLibrary()->GetEmptyString();
- }
- return RegexHelper::StringReplace(pMatch, input, pReplace);
- }
- }
- void JavascriptString::SearchValueHelper(ScriptContext* scriptContext, Var aValue, JavascriptRegExp ** ppSearchRegEx, JavascriptString ** ppSearchString)
- {
- *ppSearchRegEx = nullptr;
- *ppSearchString = nullptr;
- // When the config is enabled, the operation is handled by a Symbol function (e.g. Symbol.replace).
- if (!scriptContext->GetConfig()->IsES6RegExSymbolsEnabled()
- && JavascriptRegExp::Is(aValue))
- {
- *ppSearchRegEx = JavascriptRegExp::FromVar(aValue);
- }
- else if (JavascriptString::Is(aValue))
- {
- *ppSearchString = JavascriptString::FromVar(aValue);
- }
- else
- {
- *ppSearchString = JavascriptConversion::ToString(aValue, scriptContext);
- }
- }
- void JavascriptString::ReplaceValueHelper(ScriptContext* scriptContext, Var aValue, JavascriptFunction ** ppReplaceFn, JavascriptString ** ppReplaceString)
- {
- *ppReplaceFn = nullptr;
- *ppReplaceString = nullptr;
- if (JavascriptFunction::Is(aValue))
- {
- *ppReplaceFn = JavascriptFunction::FromVar(aValue);
- }
- else if (JavascriptString::Is(aValue))
- {
- *ppReplaceString = JavascriptString::FromVar(aValue);
- }
- else
- {
- *ppReplaceString = JavascriptConversion::ToString(aValue, scriptContext);
- }
- }
- Var JavascriptString::EntrySearch(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- PCWSTR const varName = _u("String.prototype.search");
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, varName);
- auto fallback = [&](JavascriptString* stringObj)
- {
- Var regExp = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
- if (!scriptContext->GetConfig()->IsES6RegExSymbolsEnabled())
- {
- JavascriptRegExp * regExObj = JavascriptRegExp::CreateRegEx(regExp, nullptr, scriptContext);
- return RegexHelper::RegexSearch(scriptContext, regExObj, stringObj);
- }
- else
- {
- JavascriptRegExp * regExObj = JavascriptRegExp::CreateRegExNoCoerce(regExp, nullptr, scriptContext);
- Var symbolFn = GetRegExSymbolFunction(regExObj, PropertyIds::_symbolSearch, scriptContext);
- return CallRegExSymbolFunction<1>(symbolFn, regExObj, args, varName, scriptContext);
- }
- };
- return DelegateToRegExSymbolFunction<1>(args, PropertyIds::_symbolSearch, fallback, varName, scriptContext);
- }
- template<int argCount, typename FallbackFn>
- Var JavascriptString::DelegateToRegExSymbolFunction(ArgumentReader &args, PropertyId symbolPropertyId, FallbackFn fallback, PCWSTR varName, ScriptContext* scriptContext)
- {
- if (scriptContext->GetConfig()->IsES6RegExSymbolsEnabled())
- {
- if (args.Info.Count == 0 || !JavascriptConversion::CheckObjectCoercible(args[0], scriptContext))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, varName);
- }
- if (args.Info.Count >= 2 && !JavascriptOperators::IsUndefinedOrNull(args[1]))
- {
- Var regExp = args[1];
- Var symbolFn = GetRegExSymbolFunction(regExp, symbolPropertyId, scriptContext);
- if (!JavascriptOperators::IsUndefinedOrNull(symbolFn))
- {
- return CallRegExSymbolFunction<argCount>(symbolFn, regExp, args, varName, scriptContext);
- }
- }
- }
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, varName, &pThis);
- return fallback(pThis);
- }
- Var JavascriptString::GetRegExSymbolFunction(Var regExp, PropertyId propertyId, ScriptContext* scriptContext)
- {
- return JavascriptOperators::GetPropertyNoCache(
- JavascriptOperators::ToObject(regExp, scriptContext),
- propertyId,
- scriptContext);
- }
- template<int argCount>
- Var JavascriptString::CallRegExSymbolFunction(Var fn, Var regExp, Arguments& args, PCWSTR const varName, ScriptContext* scriptContext)
- {
- if (!JavascriptConversion::IsCallable(fn))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_Invalid, varName);
- }
- RecyclableObject* fnObj = RecyclableObject::UnsafeFromVar(fn);
- return CallRegExFunction<argCount>(fnObj, regExp, args, scriptContext);
- }
- template<>
- Var JavascriptString::CallRegExFunction<1>(RecyclableObject* fnObj, Var regExp, Arguments& args, ScriptContext *scriptContext)
- {
- // args[0]: String
- return CALL_FUNCTION(scriptContext->GetThreadContext(), fnObj, CallInfo(CallFlags_Value, 2), regExp, args[0]);
- }
- template<>
- Var JavascriptString::CallRegExFunction<2>(RecyclableObject* fnObj, Var regExp, Arguments& args, ScriptContext * scriptContext)
- {
- // args[0]: String
- // args[1]: RegExp (ignored since we need to create one when the argument is "undefined")
- // args[2]: Var
- if (args.Info.Count < 3)
- {
- return CallRegExFunction<1>(fnObj, regExp, args, scriptContext);
- }
- return CALL_FUNCTION(scriptContext->GetThreadContext(), fnObj, CallInfo(CallFlags_Value, 3), regExp, args[0], args[2]);
- }
- Var JavascriptString::EntrySlice(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.slice"), &pThis);
- int len = pThis->GetLength();
- int idxStart = 0;
- int idxEnd = len;
- if (args.Info.Count > 1)
- {
- idxStart = JavascriptOperators::IsUndefinedObject(args[1], scriptContext) ? 0 : ConvertToIndex(args[1], scriptContext);
- if (args.Info.Count > 2)
- {
- idxEnd = JavascriptOperators::IsUndefinedObject(args[2], scriptContext) ? len : ConvertToIndex(args[2], scriptContext);
- }
- }
- if (idxStart < 0)
- {
- idxStart = max(len + idxStart, 0);
- }
- else if (idxStart > len)
- {
- idxStart = len;
- }
- if (idxEnd < 0)
- {
- idxEnd = max(len + idxEnd, 0);
- }
- else if (idxEnd > len )
- {
- idxEnd = len;
- }
- if (idxEnd < idxStart)
- {
- idxEnd = idxStart;
- }
- return SubstringCore(pThis, idxStart, idxEnd - idxStart, scriptContext);
- }
- Var JavascriptString::EntrySplit(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- PCWSTR const varName = _u("String.prototype.split");
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, varName);
- auto fallback = [&](JavascriptString* stringObj)
- {
- return DoStringSplit(args, callInfo, stringObj, scriptContext);
- };
- return DelegateToRegExSymbolFunction<2>(args, PropertyIds::_symbolSplit, fallback, varName, scriptContext);
- }
- Var JavascriptString::DoStringSplit(Arguments& args, CallInfo& callInfo, JavascriptString* input, ScriptContext* scriptContext)
- {
- if (args.Info.Count == 1)
- {
- JavascriptArray* ary = scriptContext->GetLibrary()->CreateArray(1);
- ary->DirectSetItemAt(0, input);
- return ary;
- }
- else
- {
- uint32 limit;
- if (args.Info.Count < 3 || JavascriptOperators::IsUndefinedObject(args[2], scriptContext))
- {
- limit = UINT_MAX;
- }
- else
- {
- limit = JavascriptConversion::ToUInt32(args[2], scriptContext);
- }
- // When the config is enabled, the operation is handled by RegExp.prototype[@@split].
- if (!scriptContext->GetConfig()->IsES6RegExSymbolsEnabled()
- && JavascriptRegExp::Is(args[1]))
- {
- return RegexHelper::RegexSplit(scriptContext, JavascriptRegExp::UnsafeFromVar(args[1]), input, limit,
- RegexHelper::IsResultNotUsed(callInfo.Flags));
- }
- else
- {
- JavascriptString* separator = JavascriptConversion::ToString(args[1], scriptContext);
- if (callInfo.Flags & CallFlags_NotUsed)
- {
- return scriptContext->GetLibrary()->GetNull();
- }
- if (!limit)
- {
- JavascriptArray* ary = scriptContext->GetLibrary()->CreateArray(0);
- return ary;
- }
- if (JavascriptOperators::GetTypeId(args[1]) == TypeIds_Undefined)
- {
- JavascriptArray* ary = scriptContext->GetLibrary()->CreateArray(1);
- ary->DirectSetItemAt(0, input);
- return ary;
- }
- return RegexHelper::StringSplit(separator, input, limit);
- }
- }
- }
- Var JavascriptString::EntrySubstring(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.substring"), &pThis);
- int len = pThis->GetLength();
- int idxStart = 0;
- int idxEnd = len;
- if (args.Info.Count > 1)
- {
- idxStart = JavascriptOperators::IsUndefinedObject(args[1], scriptContext) ? 0 : ConvertToIndex(args[1], scriptContext);
- if (args.Info.Count > 2)
- {
- idxEnd = JavascriptOperators::IsUndefinedObject(args[2], scriptContext) ? len : ConvertToIndex(args[2], scriptContext);
- }
- }
- idxStart = min(max(idxStart, 0), len);
- idxEnd = min(max(idxEnd, 0), len);
- if(idxEnd < idxStart)
- {
- //swap
- idxStart ^= idxEnd;
- idxEnd ^= idxStart;
- idxStart ^= idxEnd;
- }
- if (idxStart == 0 && idxEnd == len)
- {
- //return the string if we need to substring entire span
- return pThis;
- }
- return SubstringCore(pThis, idxStart, idxEnd - idxStart, scriptContext);
- }
- Var JavascriptString::EntrySubstr(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.substr"), &pThis);
- int len = pThis->GetLength();
- int idxStart = 0;
- int idxEnd = len;
- if (args.Info.Count > 1)
- {
- idxStart = JavascriptOperators::IsUndefinedObject(args[1], scriptContext) ? 0 : ConvertToIndex(args[1], scriptContext);
- if (args.Info.Count > 2)
- {
- idxEnd = JavascriptOperators::IsUndefinedObject(args[2], scriptContext) ? len : ConvertToIndex(args[2], scriptContext);
- }
- }
- if (idxStart < 0)
- {
- idxStart = max(len + idxStart, 0);
- }
- else if (idxStart > len)
- {
- idxStart = len;
- }
- if (idxEnd < 0)
- {
- idxEnd = idxStart;
- }
- else if (idxEnd > len - idxStart)
- {
- idxEnd = len;
- }
- else
- {
- idxEnd += idxStart;
- }
- if (idxStart == 0 && idxEnd == len)
- {
- //return the string if we need to substr entire span
- return pThis;
- }
- Assert(0 <= idxStart && idxStart <= idxEnd && idxEnd <= len);
- return SubstringCore(pThis, idxStart, idxEnd - idxStart, scriptContext);
- }
- Var JavascriptString::SubstringCore(JavascriptString* pThis, int idxStart, int span, ScriptContext* scriptContext)
- {
- return SubString::New(pThis, idxStart, span);
- }
- Var JavascriptString::EntryPadStart(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(String_Prototype_padStart);
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.padStart"), &pThis);
- return PadCore(args, pThis, true /*isPadStart*/, scriptContext);
- }
- Var JavascriptString::EntryPadEnd(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(String_Prototype_padEnd);
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.padEnd"), &pThis);
- return PadCore(args, pThis, false /*isPadStart*/, scriptContext);
- }
- JavascriptString* JavascriptString::PadCore(ArgumentReader& args, JavascriptString *mainString, bool isPadStart, ScriptContext* scriptContext)
- {
- Assert(mainString != nullptr);
- Assert(args.Info.Count > 0);
- if (args.Info.Count == 1)
- {
- return mainString;
- }
- int64 maxLength = JavascriptConversion::ToLength(args[1], scriptContext);
- charcount_t currentLength = mainString->GetLength();
- if (maxLength <= currentLength)
- {
- return mainString;
- }
- if (maxLength > JavascriptString::MaxCharLength)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_OutOfBoundString);
- }
- JavascriptString * fillerString = nullptr;
- if (args.Info.Count > 2 && !JavascriptOperators::IsUndefinedObject(args[2], scriptContext))
- {
- JavascriptString *argStr = JavascriptConversion::ToString(args[2], scriptContext);
- if (argStr->GetLength() > 0)
- {
- fillerString = argStr;
- }
- else
- {
- return mainString;
- }
- }
- if (fillerString == nullptr)
- {
- fillerString = NewWithBuffer(_u(" "), 1, scriptContext);
- }
- Assert(fillerString->GetLength() > 0);
- charcount_t fillLength = (charcount_t)(maxLength - currentLength);
- charcount_t count = fillLength / fillerString->GetLength();
- JavascriptString * finalPad = scriptContext->GetLibrary()->GetEmptyString();
- if (count > 0)
- {
- finalPad = RepeatCore(fillerString, count, scriptContext);
- fillLength -= (count * fillerString->GetLength());
- }
- if (fillLength > 0)
- {
- finalPad = Concat(finalPad, SubString::New(fillerString, 0, fillLength));
- }
- return isPadStart ? Concat(finalPad, mainString) : Concat(mainString, finalPad);
- }
- Var JavascriptString::EntryToLocaleLowerCase(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- return ToLocaleCaseHelper(args[0], false, scriptContext);
- }
- Var JavascriptString::EntryToLocaleUpperCase(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- return ToLocaleCaseHelper(args[0], true, scriptContext);
- }
- Var JavascriptString::EntryToLowerCase(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.toLowerCase"), &pThis);
- // Fast path for one character strings
- if (pThis->GetLength() == 1)
- {
- char16 inChar = pThis->GetString()[0];
- char16 outChar = inChar;
- #if DBG
- DWORD converted =
- #endif
- PlatformAgnostic::UnicodeText::ChangeStringCaseInPlace(
- PlatformAgnostic::UnicodeText::CaseFlags::CaseFlagsLower, &outChar, 1);
- Assert(converted == 1);
- return (inChar == outChar) ? pThis : scriptContext->GetLibrary()->GetCharStringCache().GetStringForChar(outChar);
- }
- return ToCaseCore(pThis, ToLower);
- }
- Var JavascriptString::EntryToString(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if(args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedString, _u("String.prototype.toString"));
- }
- AssertMsg(args.Info.Count > 0, "Negative argument count");
- JavascriptString* str = nullptr;
- if (!GetThisValueVar(args[0], &str, scriptContext))
- {
- if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
- {
- Var result;
- if (RecyclableObject::UnsafeFromVar(args[0])->InvokeBuiltInOperationRemotely(EntryToString, args, &result))
- {
- return result;
- }
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedString, _u("String.prototype.toString"));
- }
- return str;
- }
- Var JavascriptString::EntryToUpperCase(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- JavascriptString* pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.toUpperCase"), &pThis);
- // Fast path for one character strings
- if (pThis->GetLength() == 1)
- {
- char16 inChar = pThis->GetString()[0];
- char16 outChar = inChar;
- #if DBG
- DWORD converted =
- #endif
- PlatformAgnostic::UnicodeText::ChangeStringCaseInPlace(
- PlatformAgnostic::UnicodeText::CaseFlags::CaseFlagsUpper, &outChar, 1);
- Assert(converted == 1);
- return (inChar == outChar) ? pThis : scriptContext->GetLibrary()->GetCharStringCache().GetStringForChar(outChar);
- }
- return ToCaseCore(pThis, ToUpper);
- }
- Var JavascriptString::ToCaseCore(JavascriptString* pThis, ToCase toCase)
- {
- Var resultVar = nullptr;
- EnterPinnedScope((volatile void**)& pThis);
- charcount_t count = pThis->GetLength();
- const char16* inStr = pThis->GetString();
- const char16* inStrLim = inStr + count;
- const char16* i = inStr;
- // Try to find out the chars that do not need casing (in the ASCII range)
- if (toCase == ToUpper)
- {
- while (i < inStrLim)
- {
- // first range of ascii lower-case (97-122)
- // second range of ascii lower-case (223-255)
- // non-ascii chars (255+)
- if (*i >= 'a')
- {
- if (*i <= 'z') { break; }
- if (*i >= 223) { break; }
- }
- i++;
- }
- }
- else
- {
- Assert(toCase == ToLower);
- while (i < inStrLim)
- {
- // first range of ascii uppercase (65-90)
- // second range of ascii uppercase (192-222)
- // non-ascii chars (255+)
- if (*i >= 'A')
- {
- if (*i <= 'Z') { break; }
- if (*i >= 192)
- {
- if (*i < 223) { break; }
- if (*i >= 255) { break; }
- }
- }
- i++;
- }
- }
- // If no char needs casing, return immediately
- if (i == inStrLim) { return pThis; }
- // Otherwise, copy the string and start casing
- charcount_t countToCase = (charcount_t)(inStrLim - i);
- BufferStringBuilder builder(count, pThis->type->GetScriptContext());
- char16 *outStr = builder.DangerousGetWritableBuffer();
- char16* outStrLim = outStr + count;
- char16 *o = outStr;
- while (o < outStrLim)
- {
- *o++ = *inStr++;
- }
- if (toCase == ToUpper)
- {
- #if DBG
- DWORD converted =
- #endif
- PlatformAgnostic::UnicodeText::ChangeStringCaseInPlace(
- PlatformAgnostic::UnicodeText::CaseFlags::CaseFlagsUpper, outStrLim - countToCase, countToCase);
- Assert(converted == countToCase);
- }
- else
- {
- Assert(toCase == ToLower);
- #if DBG
- DWORD converted =
- #endif
- PlatformAgnostic::UnicodeText::ChangeStringCaseInPlace(
- PlatformAgnostic::UnicodeText::CaseFlags::CaseFlagsLower, outStrLim - countToCase, countToCase);
- Assert(converted == countToCase);
- }
- resultVar = builder.ToString();
- LeavePinnedScope(); // pThis
- return resultVar;
- }
- Var JavascriptString::EntryTrim(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(String_Prototype_trim);
- Assert(!(callInfo.Flags & CallFlags_New));
- //15.5.4.20 The following steps are taken:
- //1. Call CheckObjectCoercible passing the this value as its argument.
- //2. Let S be the result of calling ToString, giving it the this value as its argument.
- //3. Let T be a string value that is a copy of S with both leading and trailing white space removed. The definition of white space is the union of WhiteSpace and LineTerminator.
- //4. Return T.
- JavascriptString* pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.trim"), &pThis);
- return TrimLeftRightHelper<true /*trimLeft*/, true /*trimRight*/>(pThis, scriptContext);
- }
- Var JavascriptString::EntryTrimLeft(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- // 1.Let O be CheckObjectCoercible(this value) .
- // 2.Let S be ToString(O) .
- // 3.ReturnIfAbrupt(S).
- // 4.Let T be a String value that is a copy of S with leading white space removed. The definition of white space is the union of WhiteSpace and )LineTerminator.
- // When determining whether a Unicode code point is in Unicode general category "Zs", code unit sequences are interpreted as UTF-16 encoded code point sequences as specified in 6.1.4.
- // 5.Return T.
- JavascriptString* pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.trimLeft"), &pThis);
- return TrimLeftRightHelper< true /*trimLeft*/, false /*trimRight*/>(pThis, scriptContext);
- }
- Var JavascriptString::EntryTrimRight(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- // 1.Let O be CheckObjectCoercible(this value) .
- // 2.Let S be ToString(O) .
- // 3.ReturnIfAbrupt(S).
- // 4.Let T be a String value that is a copy of S with trailing white space removed.The definition of white space is the union of WhiteSpace and )LineTerminator.
- // When determining whether a Unicode code point is in Unicode general category "Zs", code unit sequences are interpreted as UTF - 16 encoded code point sequences as specified in 6.1.4.
- // 5.Return T.
- JavascriptString* pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.trimRight"), &pThis);
- return TrimLeftRightHelper<false /*trimLeft*/, true /*trimRight*/>(pThis, scriptContext);
- }
- template <bool trimLeft, bool trimRight>
- Var JavascriptString::TrimLeftRightHelper(JavascriptString* arg, ScriptContext* scriptContext)
- {
- static_assert(trimLeft || trimRight, "bad template instance of TrimLeftRightHelper()");
- int len = arg->GetLength();
- const char16 *string = arg->GetString();
- int idxStart = 0;
- if (trimLeft)
- {
- for (; idxStart < len; idxStart++)
- {
- char16 ch = string[idxStart];
- if (IsWhiteSpaceCharacter(ch))
- {
- continue;
- }
- break;
- }
- if (len == idxStart)
- {
- return (scriptContext->GetLibrary()->GetEmptyString());
- }
- }
- int idxEnd = len - 1;
- if (trimRight)
- {
- for (; idxEnd >= 0; idxEnd--)
- {
- char16 ch = string[idxEnd];
- if (IsWhiteSpaceCharacter(ch))
- {
- continue;
- }
- break;
- }
- if (!trimLeft)
- {
- if (idxEnd < 0)
- {
- Assert(idxEnd == -1);
- return (scriptContext->GetLibrary()->GetEmptyString());
- }
- }
- else
- {
- Assert(idxEnd >= 0);
- }
- }
- return SubstringCore(arg, idxStart, idxEnd - idxStart + 1, scriptContext);
- }
- ///----------------------------------------------------------------------------
- /// Repeat() returns a new string equal to the toString(this) repeated n times,
- /// as described in (ES6.0: S21.1.3.13).
- ///----------------------------------------------------------------------------
- Var JavascriptString::EntryRepeat(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(String_Prototype_repeat);
- JavascriptString* pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.repeat"), &pThis);
- charcount_t count = 0;
- if (args.Info.Count > 1)
- {
- if (!JavascriptOperators::IsUndefinedObject(args[1], scriptContext))
- {
- double countDbl = JavascriptConversion::ToInteger(args[1], scriptContext);
- if (JavascriptNumber::IsPosInf(countDbl) || countDbl < 0.0)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange, _u("String.prototype.repeat"));
- }
- count = NumberUtilities::LuFromDblNearest(countDbl);
- }
- }
- if (count == 0 || pThis->GetLength() == 0)
- {
- return scriptContext->GetLibrary()->GetEmptyString();
- }
- else if (count == 1)
- {
- return pThis;
- }
- return RepeatCore(pThis, count, scriptContext);
- }
- JavascriptString* JavascriptString::RepeatCore(JavascriptString* currentString, charcount_t count, ScriptContext* scriptContext)
- {
- Assert(currentString != nullptr);
- Assert(currentString->GetLength() > 0);
- Assert(count > 0);
- const char16* currentRawString = currentString->GetString();
- charcount_t currentLength = currentString->GetLength();
- charcount_t finalBufferCount = UInt32Math::Add(UInt32Math::Mul(count, currentLength), 1);
- char16* buffer = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), char16, finalBufferCount);
- if (currentLength == 1)
- {
- wmemset(buffer, currentRawString[0], finalBufferCount - 1);
- buffer[finalBufferCount - 1] = '\0';
- }
- else
- {
- char16* bufferDst = buffer;
- size_t bufferDstSize = finalBufferCount;
- AnalysisAssert(bufferDstSize > currentLength);
- for (charcount_t i = 0; i < count; i += 1)
- {
- js_wmemcpy_s(bufferDst, bufferDstSize, currentRawString, currentLength);
- bufferDst += currentLength;
- bufferDstSize -= currentLength;
- }
- Assert(bufferDstSize == 1);
- *bufferDst = '\0';
- }
- return JavascriptString::NewWithBuffer(buffer, finalBufferCount - 1, scriptContext);
- }
- ///----------------------------------------------------------------------------
- /// StartsWith() returns true if the given string matches the beginning of the
- /// substring starting at the given position in toString(this), as described
- /// in (ES6.0: S21.1.3.18).
- ///----------------------------------------------------------------------------
- Var JavascriptString::EntryStartsWith(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(String_Prototype_startsWith);
- ENTER_PINNED_SCOPE(JavascriptString, pThis);
- ENTER_PINNED_SCOPE(JavascriptString, pSearch);
- GetThisAndSearchStringArguments(args, scriptContext, _u("String.prototype.startsWith"), &pThis, &pSearch, false);
- const char16* thisStr = pThis->GetString();
- int thisStrLen = pThis->GetLength();
- const char16* searchStr = pSearch->GetString();
- int searchStrLen = pSearch->GetLength();
- int startPosition = 0;
- if (args.Info.Count > 2)
- {
- if (!JavascriptOperators::IsUndefinedObject(args[2], scriptContext))
- {
- startPosition = ConvertToIndex(args[2], scriptContext); // this is to adjust corner cases like MAX_VALUE
- startPosition = min(max(startPosition, 0), thisStrLen);
- }
- }
- // Avoid signed 32-bit int overflow if startPosition is large by subtracting searchStrLen from thisStrLen instead of
- // adding searchStrLen and startPosition. The subtraction cannot underflow because maximum string length is
- // MaxCharCount == INT_MAX-1. I.e. the RHS can be == 0 - (INT_MAX-1) == 1 - INT_MAX which would not underflow.
- if (startPosition <= thisStrLen - searchStrLen)
- {
- Assert(searchStrLen <= thisStrLen - startPosition);
- if (wmemcmp(thisStr + startPosition, searchStr, searchStrLen) == 0)
- {
- return scriptContext->GetLibrary()->GetTrue();
- }
- }
- LEAVE_PINNED_SCOPE(); // pSearch
- LEAVE_PINNED_SCOPE(); // pThis
- return scriptContext->GetLibrary()->GetFalse();
- }
- ///----------------------------------------------------------------------------
- /// EndsWith() returns true if the given string matches the end of the
- /// substring ending at the given position in toString(this), as described
- /// in (ES6.0: S21.1.3.7).
- ///----------------------------------------------------------------------------
- Var JavascriptString::EntryEndsWith(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(String_Prototype_endsWith);
- ENTER_PINNED_SCOPE(JavascriptString, pThis);
- ENTER_PINNED_SCOPE(JavascriptString, pSearch);
- GetThisAndSearchStringArguments(args, scriptContext, _u("String.prototype.endsWith"), &pThis, &pSearch, false);
- const char16* thisStr = pThis->GetString();
- int thisStrLen = pThis->GetLength();
- const char16* searchStr = pSearch->GetString();
- int searchStrLen = pSearch->GetLength();
- int endPosition = thisStrLen;
- if (args.Info.Count > 2)
- {
- if (!JavascriptOperators::IsUndefinedObject(args[2], scriptContext))
- {
- endPosition = ConvertToIndex(args[2], scriptContext); // this is to adjust corner cases like MAX_VALUE
- endPosition = min(max(endPosition, 0), thisStrLen);
- }
- }
- int startPosition = endPosition - searchStrLen;
- if (startPosition >= 0)
- {
- Assert(startPosition <= thisStrLen);
- Assert(searchStrLen <= thisStrLen - startPosition);
- if (wmemcmp(thisStr + startPosition, searchStr, searchStrLen) == 0)
- {
- return scriptContext->GetLibrary()->GetTrue();
- }
- }
- LEAVE_PINNED_SCOPE(); // pSearch
- LEAVE_PINNED_SCOPE(); // pThis
- return scriptContext->GetLibrary()->GetFalse();
- }
- ///----------------------------------------------------------------------------
- /// Includes() returns true if the given string matches any substring (of the
- /// same length) of the substring starting at the given position in
- /// toString(this), as described in (ES6.0 (draft 33): S21.1.3.7).
- ///----------------------------------------------------------------------------
- Var JavascriptString::EntryIncludes(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(String_Prototype_contains);
- return JavascriptBoolean::ToVar(IndexOf(args, scriptContext, _u("String.prototype.includes"), false) != -1, scriptContext);
- }
- Var JavascriptString::EntryValueOf(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if(args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedString, _u("String.prototype.valueOf"));
- }
- AssertMsg(args.Info.Count > 0, "Negative argument count");
- JavascriptString* str = nullptr;
- if (!GetThisValueVar(args[0], &str, scriptContext))
- {
- if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
- {
- Var result;
- if (RecyclableObject::UnsafeFromVar(args[0])->InvokeBuiltInOperationRemotely(EntryValueOf, args, &result))
- {
- return result;
- }
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedString, _u("String.prototype.valueOf"));
- }
- return str;
- }
- Var JavascriptString::EntrySymbolIterator(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedString, _u("String.prototype[Symbol.iterator]"));
- }
- AssertMsg(args.Info.Count > 0, "Negative argument count");
- if (!JavascriptConversion::CheckObjectCoercible(args[0], scriptContext))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("String.prototype[Symbol.iterator]"));
- }
- JavascriptString* str = JavascriptConversion::ToString(args[0], scriptContext);
- return scriptContext->GetLibrary()->CreateStringIterator(str);
- }
- const char16 * JavascriptString::GetSz()
- {
- Assert(m_pszValue[m_charLength] == _u('\0'));
- return m_pszValue;
- }
- const char16 * JavascriptString::GetString()
- {
- if (!this->IsFinalized())
- {
- this->GetSz();
- Assert(m_pszValue);
- }
- return m_pszValue;
- }
- void const * JavascriptString::GetOriginalStringReference()
- {
- // Just return the string buffer
- return GetString();
- }
- size_t JavascriptString::GetAllocatedByteCount() const
- {
- if (!this->IsFinalized())
- {
- return 0;
- }
- return this->m_charLength * sizeof(WCHAR);
- }
- bool JavascriptString::IsSubstring() const
- {
- return false;
- }
- bool JavascriptString::IsNegZero(JavascriptString *string)
- {
- return string->GetLength() == 2 && wmemcmp(string->GetString(), _u("-0"), 2) == 0;
- }
- void JavascriptString::FinishCopy(__inout_xcount(m_charLength) char16 *const buffer, StringCopyInfoStack &nestedStringTreeCopyInfos)
- {
- while (!nestedStringTreeCopyInfos.IsEmpty())
- {
- const StringCopyInfo copyInfo(nestedStringTreeCopyInfos.Pop());
- Assert(copyInfo.SourceString()->GetLength() <= GetLength());
- Assert(copyInfo.DestinationBuffer() >= buffer);
- Assert(copyInfo.DestinationBuffer() <= buffer + (GetLength() - copyInfo.SourceString()->GetLength()));
- copyInfo.SourceString()->Copy(copyInfo.DestinationBuffer(), nestedStringTreeCopyInfos, 0);
- }
- }
- void JavascriptString::CopyVirtual(
- _Out_writes_(m_charLength) char16 *const buffer,
- StringCopyInfoStack &nestedStringTreeCopyInfos,
- const byte recursionDepth)
- {
- Assert(buffer);
- Assert(!this->IsFinalized()); // CopyVirtual should only be called for unfinalized buffers
- CopyHelper(buffer, GetString(), GetLength());
- }
- char16* JavascriptString::GetSzCopy()
- {
- return AllocateLeafAndCopySz(this->GetScriptContext()->GetRecycler(), GetString(), GetLength());
- }
- LPCWSTR JavascriptString::GetSzCopy(ArenaAllocator* alloc)
- {
- return AllocateAndCopySz(alloc, GetString(), GetLength());
- }
- /*
- Table generated using the following:
- var invalidValue = 37;
- function toStringTable()
- {
- var stringTable = new Array(128);
- for(var i = 0; i < 128; i++)
- {
- var ch = i;
- if ('0'.charCodeAt(0) <= ch && '9'.charCodeAt(0) >= ch)
- ch -= '0'.charCodeAt(0);
- else if ('A'.charCodeAt(0) <= ch && 'Z'.charCodeAt(0) >= ch)
- ch -= 'A'.charCodeAt(0) - 10;
- else if ('a'.charCodeAt(0) <= ch && 'z'.charCodeAt(0) >= ch)
- ch -= 'a'.charCodeAt(0) - 10;
- else
- ch = 37;
- stringTable[i] = ch;
- }
- WScript.Echo("{" + stringTable + "}");
- }
- toStringTable();*/
- const char JavascriptString::stringToIntegerMap[] = {
- 37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37
- ,37,37,37,37,37,37,37,37,0,1,2,3,4,5,6,7,8,9,37,37,37,37,37,37,37,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,
- 28,29,30,31,32,33,34,35,37,37,37,37,37,37,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,
- 37,37,37,37,37};
- /*
- Table generated using the following:
- function logMaxUintTable()
- {
- var MAX_UINT = 4294967295;
- var logTable = new Array(37);
- logTable[0] = 0;
- logTable[1] = 0;
- for(var i = 2; i < logTable.length; i++)
- {
- logTable[i] = Math.floor(Math.log(MAX_UINT + 1) / Math.log(i));
- }
- WScript.Echo("{" + logTable + "}");
- }
- logMaxUintTable();
- */
- const uint8 JavascriptString::maxUintStringLengthTable[] =
- { 0,0,32,20,16,13,12,11,10,10,9,9,8,8,8,8,8,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6 };
- // NumberUtil::FIntRadStrToDbl and parts of GlobalObject::EntryParseInt were refactored into ToInteger
- Var JavascriptString::ToInteger(int radix)
- {
- AssertMsg(radix == 0 || radix >= 2 && radix <= 36, "'radix' is invalid");
- const char16* pchStart = GetString();
- const char16* pchEnd = pchStart + m_charLength;
- const char16 *pch = this->GetScriptContext()->GetCharClassifier()->SkipWhiteSpace(pchStart, pchEnd);
- bool isNegative = false;
- if (pch < pchEnd)
- {
- switch (*pch)
- {
- case '-':
- isNegative = true;
- // Fall through.
- case '+':
- pch++;
- break;
- }
- }
- if (0 == radix)
- {
- if (pch < pchEnd && '0' != pch[0])
- {
- radix = 10;
- }
- else if (pchEnd - pch >= 2 && ('x' == pch[1] || 'X' == pch[1]))
- {
- radix = 16;
- pch += 2;
- }
- else
- {
- // ES5's 'parseInt' does not allow treating a string beginning with a '0' as an octal value. ES3 does not specify a
- // behavior
- radix = 10;
- }
- }
- else if (16 == radix)
- {
- if(pchEnd - pch >= 2 && '0' == pch[0] && ('x' == pch[1] || 'X' == pch[1]))
- {
- pch += 2;
- }
- }
- Assert(radix <= _countof(maxUintStringLengthTable));
- Assert(pchEnd >= pch);
- size_t length = pchEnd - pch;
- const char16 *const pchMin = pch;
- __analysis_assume(radix < _countof(maxUintStringLengthTable));
- if(length <= maxUintStringLengthTable[radix])
- {
- // Use uint32 as integer being parsed - much faster than BigInt
- uint32 value = 0;
- for ( ; pch < pchEnd ; pch++)
- {
- char16 ch = *pch;
- if(ch >= _countof(stringToIntegerMap) || (ch = stringToIntegerMap[ch]) >= radix)
- {
- break;
- }
- uint32 beforeValue = value;
- value = value * radix + ch;
- AssertMsg(value >= beforeValue, "uint overflow");
- }
- if(pchMin == pch)
- {
- return GetScriptContext()->GetLibrary()->GetNaN();
- }
- if(isNegative)
- {
- // negative zero can only be represented by doubles
- if(value <= INT_MAX && value != 0)
- {
- int32 result = -((int32)value);
- return JavascriptNumber::ToVar(result, this->GetScriptContext());
- }
- double result = -((double)(value));
- return JavascriptNumber::New(result, this->GetScriptContext());
- }
- return JavascriptNumber::ToVar(value, this->GetScriptContext());
- }
- BigInt bi;
- for ( ; pch < pchEnd ; pch++)
- {
- char16 ch = *pch;
- if(ch >= _countof(stringToIntegerMap) || (ch = stringToIntegerMap[ch]) >= radix)
- {
- break;
- }
- if (!bi.FMulAdd(radix, ch))
- {
- //Mimic IE8 which threw an OutOfMemory exception in this case.
- JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
- }
- // If we ever have more than 32 ulongs, the result must be infinite.
- if (bi.Clu() > 32)
- {
- Var result = isNegative ?
- GetScriptContext()->GetLibrary()->GetNegativeInfinite() :
- GetScriptContext()->GetLibrary()->GetPositiveInfinite();
- return result;
- }
- }
- if (pchMin == pch)
- {
- return GetScriptContext()->GetLibrary()->GetNaN();
- }
- // Convert to a double.
- double result = bi.GetDbl();
- if(isNegative)
- {
- result = -result;
- }
- return Js::JavascriptNumber::ToVarIntCheck(result, GetScriptContext());
- }
- bool JavascriptString::ToDouble(double * result)
- {
- const char16* pch;
- int32 len = this->m_charLength;
- if (0 == len)
- {
- *result = 0;
- return true;
- }
- if (1 == len && NumberUtilities::IsDigit(this->GetString()[0]))
- {
- *result = (double)(this->GetString()[0] - '0');
- return true;
- }
- // TODO: Use GetString here instead of GetSz (need to modify DblFromHex and StrToDbl to take a length)
- for (pch = this->GetSz(); IsWhiteSpaceCharacter(*pch); pch++)
- ;
- if (0 == *pch)
- {
- *result = 0;
- return true;
- }
- bool isNumericLiteral = false;
- if (*pch == '0')
- {
- const char16 *pchT = pch + 2;
- switch (pch[1])
- {
- case 'x':
- case 'X':
- *result = NumberUtilities::DblFromHex(pchT, &pch);
- isNumericLiteral = true;
- break;
- case 'o':
- case 'O':
- *result = NumberUtilities::DblFromOctal(pchT, &pch);
- isNumericLiteral = true;
- break;
- case 'b':
- case 'B':
- *result = NumberUtilities::DblFromBinary(pchT, &pch);
- isNumericLiteral = true;
- break;
- }
- if (pchT == pch && isNumericLiteral)
- {
- *result = JavascriptNumber::NaN;
- return false;
- }
- }
- if (!isNumericLiteral)
- {
- *result = NumberUtilities::StrToDbl(pch, &pch, GetScriptContext());
- }
- while (IsWhiteSpaceCharacter(*pch))
- pch++;
- if (pch != this->m_pszValue + len)
- {
- *result = JavascriptNumber::NaN;
- return false;
- }
- return true;
- }
- double JavascriptString::ToDouble()
- {
- double result;
- this->ToDouble(&result);
- return result;
- }
- bool JavascriptString::Equals(Var aLeft, Var aRight)
- {
- return JavascriptStringHelpers<JavascriptString>::Equals(aLeft, aRight);
- }
- //
- // LessThan implements algorithm of ES5 11.8.5 step 4
- // returns false for same string pattern
- //
- bool JavascriptString::LessThan(Var aLeft, Var aRight)
- {
- AssertMsg(JavascriptString::Is(aLeft) && JavascriptString::Is(aRight), "string LessThan");
- JavascriptString *leftString = JavascriptString::FromVar(aLeft);
- JavascriptString *rightString = JavascriptString::FromVar(aRight);
- if (JavascriptString::strcmp(leftString, rightString) < 0)
- {
- return true;
- }
- return false;
- }
- // thisStringValue(value) abstract operation as defined in ES6.0 (Draft 25) Section 21.1.3
- BOOL JavascriptString::GetThisValueVar(Var aValue, JavascriptString** pString, ScriptContext* scriptContext)
- {
- Assert(pString);
- // 1. If Type(value) is String, return value.
- if (JavascriptString::Is(aValue))
- {
- *pString = JavascriptString::FromVar(aValue);
- return TRUE;
- }
- // 2. If Type(value) is Object and value has a [[StringData]] internal slot
- else if ( JavascriptStringObject::Is(aValue))
- {
- JavascriptStringObject* pStringObj = JavascriptStringObject::FromVar(aValue);
- // a. Let s be the value of value's [[StringData]] internal slot.
- // b. If s is not undefined, then return s.
- *pString = pStringObj->Unwrap();
- *pString = JavascriptString::FromVar(CrossSite::MarshalVar(scriptContext,
- *pString, pStringObj->GetScriptContext()));
- return TRUE;
- }
- // 3. Throw a TypeError exception.
- // Note: We don't throw a TypeError here, choosing to return FALSE and let the caller throw the error
- return FALSE;
- }
- #ifdef TAGENTRY
- #undef TAGENTRY
- #endif
- #define TAGENTRY(name, ...) \
- Var JavascriptString::Entry##name(RecyclableObject* function, CallInfo callInfo, ...) \
- { \
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); \
- \
- ARGUMENTS(args, callInfo); \
- ScriptContext* scriptContext = function->GetScriptContext(); \
- \
- Assert(!(callInfo.Flags & CallFlags_New)); \
- \
- return StringBracketHelper(args, scriptContext, __VA_ARGS__); \
- }
- #include "JavascriptStringTagEntries.h"
- #undef TAGENTRY
- Var JavascriptString::StringBracketHelper(Arguments args, ScriptContext *scriptContext, __in_ecount(cchTag) char16 const *pszTag,
- charcount_t cchTag, __in_ecount_opt(cchProp) char16 const *pszProp, charcount_t cchProp)
- {
- charcount_t cchThis;
- charcount_t cchPropertyValue;
- charcount_t cchTotalChars;
- charcount_t ich;
- JavascriptString * pThis = nullptr;
- JavascriptString * pPropertyValue = nullptr;
- const char16 * propertyValueStr = nullptr;
- uint quotesCount = 0;
- const char16 quotStr[] = _u(""");
- const charcount_t quotStrLen = _countof(quotStr) - 1;
- bool ES6FixesEnabled = scriptContext->GetConfig()->IsES6StringPrototypeFixEnabled();
- // Assemble the component pieces of a string tag function (ex: String.prototype.link).
- // In the general case, result is as below:
- //
- // pszProp = _u("href");
- // pszTag = _u("a");
- // pThis = JavascriptString::FromVar(args[0]);
- // pPropertyValue = JavascriptString::FromVar(args[1]);
- //
- // pResult = _u("<a href=\"[[pPropertyValue]]\">[[pThis]]</a>");
- //
- // cchTotalChars = 5 // <></>
- // + cchTag * 2 // a
- // + cchProp // href
- // + 4 // _=""
- // + cchPropertyValue
- // + cchThis;
- //
- // Note: With ES6FixesEnabled, we need to escape quote characters (_u('"')) in pPropertyValue.
- // Note: Without ES6FixesEnabled, the tag and prop strings should be capitalized.
- if(args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedString);
- }
- if (ES6FixesEnabled)
- {
- if (!JavascriptConversion::CheckObjectCoercible(args[0], scriptContext))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, pszTag);
- }
- }
- pThis = JavascriptOperators::TryFromVar<JavascriptString>(args[0]);
- if (!pThis)
- {
- pThis = JavascriptConversion::ToString(args[0], scriptContext);
- }
- cchThis = pThis->GetLength();
- cchTotalChars = UInt32Math::Add(cchTag, cchTag);
- // 5 is for the <></> characters
- cchTotalChars = UInt32Math::Add(cchTotalChars, 5);
- if (nullptr != pszProp)
- {
- // Need one string argument.
- if (args.Info.Count >= 2)
- {
- pPropertyValue = JavascriptOperators::TryFromVar<JavascriptString>(args[1]);
- if (!pPropertyValue)
- {
- pPropertyValue = JavascriptConversion::ToString(args[1], scriptContext);
- }
- }
- else
- {
- pPropertyValue = scriptContext->GetLibrary()->GetUndefinedDisplayString();
- }
- cchPropertyValue = pPropertyValue->GetLength();
- propertyValueStr = pPropertyValue->GetString();
- if (ES6FixesEnabled)
- {
- // Count the number of " characters we need to escape.
- for (ich = 0; ich < cchPropertyValue; ich++)
- {
- if (propertyValueStr[ich] == _u('"'))
- {
- ++quotesCount;
- }
- }
- }
- cchTotalChars = UInt32Math::Add(cchTotalChars, cchProp);
- // 4 is for the _="" characters
- cchTotalChars = UInt32Math::Add(cchTotalChars, 4);
- if (ES6FixesEnabled)
- {
- // Account for the " escaping (")
- cchTotalChars = UInt32Math::Add(cchTotalChars, UInt32Math::Mul(quotesCount, quotStrLen)) - quotesCount;
- }
- }
- else
- {
- cchPropertyValue = 0;
- cchProp = 0;
- }
- cchTotalChars = UInt32Math::Add(cchTotalChars, cchThis);
- cchTotalChars = UInt32Math::Add(cchTotalChars, cchPropertyValue);
- if (!IsValidCharCount(cchTotalChars) || cchTotalChars < cchThis || cchTotalChars < cchPropertyValue)
- {
- Js::JavascriptError::ThrowOutOfMemoryError(scriptContext);
- }
- BufferStringBuilder builder(cchTotalChars, scriptContext);
- char16 *pResult = builder.DangerousGetWritableBuffer();
- *pResult++ = _u('<');
- for (ich = 0; ich < cchTag; ich++)
- {
- *pResult++ = ES6FixesEnabled ? pszTag[ich] : towupper(pszTag[ich]);
- }
- if (nullptr != pszProp)
- {
- *pResult++ = _u(' ');
- for (ich = 0; ich < cchProp; ich++)
- {
- *pResult++ = ES6FixesEnabled ? pszProp[ich] : towupper(pszProp[ich]);
- }
- *pResult++ = _u('=');
- *pResult++ = _u('"');
- Assert(propertyValueStr != nullptr);
- if (!ES6FixesEnabled || quotesCount == 0)
- {
- js_wmemcpy_s(pResult,
- cchTotalChars - (pResult - builder.DangerousGetWritableBuffer() + 1),
- propertyValueStr,
- cchPropertyValue);
- pResult += cchPropertyValue;
- }
- else {
- for (ich = 0; ich < cchPropertyValue; ich++)
- {
- if (propertyValueStr[ich] == _u('"'))
- {
- charcount_t destLengthLeft = (cchTotalChars - (charcount_t)(pResult - builder.DangerousGetWritableBuffer() + 1));
- // Copy the quote string into result beginning at the index where the quote would appear
- js_wmemcpy_s(pResult,
- destLengthLeft,
- quotStr,
- quotStrLen);
- // Move result ahead by the length of the quote string
- pResult += quotStrLen;
- // We ate one of the quotes
- quotesCount--;
- // We only need to check to see if we have no more quotes after eating a quote
- if (quotesCount == 0)
- {
- // Skip the quote character.
- // Note: If ich is currently the last character (cchPropertyValue-1), it becomes cchPropertyValue after incrementing.
- // At that point, cchPropertyValue - ich == 0 so we will not increment pResult and will call memcpy for zero bytes.
- ich++;
- // Copy the rest from the property value string starting at the index after the last quote
- js_wmemcpy_s(pResult,
- destLengthLeft - quotStrLen,
- propertyValueStr + ich,
- cchPropertyValue - ich);
- // Move result ahead by the length of the rest of the property string
- pResult += (cchPropertyValue - ich);
- break;
- }
- }
- else
- {
- // Each non-quote character just gets copied into result string
- *pResult++ = propertyValueStr[ich];
- }
- }
- }
- *pResult++ = _u('"');
- }
- *pResult++ = _u('>');
- const char16 *pThisString = pThis->GetString();
- js_wmemcpy_s(pResult, cchTotalChars - (pResult - builder.DangerousGetWritableBuffer() + 1), pThisString, cchThis);
- pResult += cchThis;
- *pResult++ = _u('<');
- *pResult++ = _u('/');
- for (ich = 0; ich < cchTag; ich++)
- {
- *pResult++ = ES6FixesEnabled ? pszTag[ich] : towupper(pszTag[ich]);
- }
- *pResult++ = _u('>');
- // Assert we ended at the right place.
- AssertMsg((charcount_t)(pResult - builder.DangerousGetWritableBuffer()) == cchTotalChars, "Exceeded allocated string limit");
- return builder.ToString();
- }
- Var JavascriptString::ToLocaleCaseHelper(Var thisObj, bool toUpper, ScriptContext *scriptContext)
- {
- using namespace PlatformAgnostic::UnicodeText;
- JavascriptString * pThis = JavascriptOperators::TryFromVar<JavascriptString>(thisObj);
- if (!pThis)
- {
- pThis = JavascriptConversion::ToString(thisObj, scriptContext);
- }
- uint32 strLength = pThis->GetLength();
- if (strLength == 0)
- {
- return pThis;
- }
- // Get the number of chars in the mapped string.
- CaseFlags caseFlags = (toUpper ? CaseFlags::CaseFlagsUpper : CaseFlags::CaseFlagsLower);
- const char16* str = pThis->GetString();
- ApiError err = ApiError::NoError;
- int32 count = PlatformAgnostic::UnicodeText::ChangeStringLinguisticCase(caseFlags, str, strLength, nullptr, 0, &err);
- if (count <= 0)
- {
- AssertMsg(err != ApiError::NoError, "LCMapString failed");
- Throw::InternalError();
- }
- BufferStringBuilder builder(count, scriptContext);
- char16* stringBuffer = builder.DangerousGetWritableBuffer();
- int count1 = PlatformAgnostic::UnicodeText::ChangeStringLinguisticCase(caseFlags, str, strLength, stringBuffer, count, &err);
- if (count1 <= 0)
- {
- AssertMsg(err != ApiError::NoError, "LCMapString failed");
- Throw::InternalError();
- }
- return builder.ToString();
- }
- int JavascriptString::IndexOfUsingJmpTable(JmpTable jmpTable, const char16* inputStr, charcount_t len, const char16* searchStr, int searchLen, int position)
- {
- int result = -1;
- const char16 searchLast = searchStr[searchLen-1];
- uint32 lMatchedJump = searchLen;
- if (jmpTable[searchLast].shift > 0)
- {
- lMatchedJump = jmpTable[searchLast].shift;
- }
- char16 const * p = inputStr + position + searchLen-1;
- WCHAR c;
- while(p < inputStr + len)
- {
- // first character match, keep checking
- if (*p == searchLast)
- {
- if ( wmemcmp(p-searchLen+1, searchStr, searchLen) == 0 )
- {
- break;
- }
- p += lMatchedJump;
- }
- else
- {
- c = *p;
- if ( 0 == ( c & ~0x7f ) && jmpTable[c].shift != 0 )
- {
- p += jmpTable[c].shift;
- }
- else
- {
- p += searchLen;
- }
- }
- }
- if (p >= inputStr+position && p < inputStr + len)
- {
- result = (int)(p - inputStr) - searchLen + 1;
- }
- return result;
- }
- int JavascriptString::LastIndexOfUsingJmpTable(JmpTable jmpTable, const char16* inputStr, charcount_t len, const char16* searchStr, charcount_t searchLen, charcount_t position)
- {
- const char16 searchFirst = searchStr[0];
- uint32 lMatchedJump = searchLen;
- if (jmpTable[searchFirst].shift > 0)
- {
- lMatchedJump = jmpTable[searchFirst].shift;
- }
- WCHAR c;
- char16 const * p = inputStr + min(len - searchLen, position);
- while(p >= inputStr)
- {
- // first character match, keep checking
- if (*p == searchFirst)
- {
- if ( wmemcmp(p, searchStr, searchLen) == 0 )
- {
- break;
- }
- p -= lMatchedJump;
- }
- else
- {
- c = *p;
- if ( 0 == ( c & ~0x7f ) && jmpTable[c].shift != 0 )
- {
- p -= jmpTable[c].shift;
- }
- else
- {
- p -= searchLen;
- }
- }
- }
- return ((p >= inputStr) ? (int)(p - inputStr) : -1);
- }
- bool JavascriptString::BuildLastCharForwardBoyerMooreTable(JmpTable jmpTable, const char16* searchStr, int searchLen)
- {
- AssertMsg(searchLen >= 1, "Table for non-empty string");
- memset(jmpTable, 0, sizeof(JmpTable));
- const char16 * p2 = searchStr + searchLen - 1;
- const char16 * const begin = searchStr;
- // Determine if we can do a partial ASCII Boyer-Moore
- while (p2 >= begin)
- {
- WCHAR c = *p2;
- if ( 0 == ( c & ~0x7f ))
- {
- if ( jmpTable[c].shift == 0 )
- {
- jmpTable[c].shift = (uint32)(searchStr + searchLen - 1 - p2);
- }
- }
- else
- {
- return false;
- }
- p2--;
- }
- return true;
- }
- bool JavascriptString::BuildFirstCharBackwardBoyerMooreTable(JmpTable jmpTable, const char16* searchStr, int searchLen)
- {
- AssertMsg(searchLen >= 1, "Table for non-empty string");
- memset(jmpTable, 0, sizeof(JmpTable));
- const char16 * p2 = searchStr;
- const char16 * const end = searchStr + searchLen;
- // Determine if we can do a partial ASCII Boyer-Moore
- while (p2 < end)
- {
- WCHAR c = *p2;
- if ( 0 == ( c & ~0x7f ))
- {
- if ( jmpTable[c].shift == 0 )
- {
- jmpTable[c].shift = (uint32)(p2 - searchStr);
- }
- }
- else
- {
- return false;
- }
- p2++;
- }
- return true;
- }
- uint JavascriptString::strstr(JavascriptString *string, JavascriptString *substring, bool useBoyerMoore, uint start)
- {
- uint i;
- const char16 *stringOrig = string->GetString();
- uint stringLenOrig = string->GetLength();
- const char16 *stringSz = stringOrig + start;
- const char16 *substringSz = substring->GetString();
- uint stringLen = stringLenOrig - start;
- uint substringLen = substring->GetLength();
- if (useBoyerMoore && substringLen > 2)
- {
- JmpTable jmpTable;
- bool fAsciiJumpTable = BuildLastCharForwardBoyerMooreTable(jmpTable, substringSz, substringLen);
- if (fAsciiJumpTable)
- {
- int result = IndexOfUsingJmpTable(jmpTable, stringOrig, stringLenOrig, substringSz, substringLen, start);
- if (result != -1)
- {
- return result;
- }
- else
- {
- return (uint)-1;
- }
- }
- }
- if (stringLen >= substringLen)
- {
- // If substring is empty, it matches anything...
- if (substringLen == 0)
- {
- return 0;
- }
- for (i = 0; i <= stringLen - substringLen; i++)
- {
- // Quick check for first character.
- if (stringSz[i] == substringSz[0])
- {
- if (substringLen == 1 || wmemcmp(stringSz + i + 1, substringSz + 1, substringLen - 1) == 0)
- {
- return i + start;
- }
- }
- }
- }
- return (uint)-1;
- }
- int JavascriptString::strcmp(JavascriptString *string1, JavascriptString *string2)
- {
- uint string1Len = string1->GetLength();
- uint string2Len = string2->GetLength();
- // We want to pin the strings string1 and string2 because flattening of any of these strings could cause a GC and result in the other string getting collected if it was optimized
- // away by the compiler. We would normally have called the EnterPinnedScope/LeavePinnedScope methods here but it adds extra call instructions to the assembly code. As Equals
- // methods could get called a lot of times this can show up as regressions in benchmarks.
- volatile Js::JavascriptString** keepAliveString1 = (volatile Js::JavascriptString**)& string1;
- volatile Js::JavascriptString** keepAliveString2 = (volatile Js::JavascriptString**)& string2;
- auto keepAliveLambda = [&]() {
- UNREFERENCED_PARAMETER(keepAliveString1);
- UNREFERENCED_PARAMETER(keepAliveString2);
- };
- int result = wmemcmp(string1->GetString(), string2->GetString(), min(string1Len, string2Len));
- return (result == 0) ? (int)(string1Len - string2Len) : result;
- }
- /*static*/ charcount_t JavascriptString::SafeSzSize(charcount_t cch)
- {
- // JavascriptString::MaxCharLength is valid; however, we are incrementing below by 1 and want to make sure we aren't overflowing
- // Nor going outside of valid range.
- if (cch >= JavascriptString::MaxCharLength)
- {
- Throw::OutOfMemory();
- }
- // Compute cch + 1, checking for overflow
- ++cch;
- return cch;
- }
- charcount_t JavascriptString::SafeSzSize() const
- {
- return SafeSzSize(GetLength());
- }
- /*static*/ __ecount(length+1) char16* JavascriptString::AllocateLeafAndCopySz(__in Recycler* recycler, __in_ecount(length) const char16* content, charcount_t length)
- {
- // Note: Intentionally not using SafeSzSize nor hoisting common
- // sub-expression "length + 1" into a local variable otherwise
- // Prefast gets confused and cannot track buffer's length.
- // JavascriptString::MaxCharLength is valid; however, we are incrementing below by 1 and want to make sure we aren't overflowing
- // Nor going outside of valid range.
- if (length >= JavascriptString::MaxCharLength)
- {
- Throw::OutOfMemory();
- }
- charcount_t bufLen = length + 1;
- // Allocate recycler memory to store the string plus a terminating NUL
- char16* buffer = RecyclerNewArrayLeaf(recycler, char16, bufLen);
- js_wmemcpy_s(buffer, bufLen, content, length);
- buffer[length] = _u('\0');
- return buffer;
- }
- /*static*/ __ecount(length+1) char16* JavascriptString::AllocateAndCopySz(__in ArenaAllocator* arena, __in_ecount(length) const char16* content, charcount_t length)
- {
- // Note: Intentionally not using SafeSzSize nor hoisting common
- // sub-expression "length + 1" into a local variable otherwise
- // Prefast gets confused and cannot track buffer's length.
- // JavascriptString::MaxCharLength is valid; however, we are incrementing below by 1 and want to make sure we aren't overflowing
- // Nor going outside of valid range.
- if (length >= JavascriptString::MaxCharLength)
- {
- Throw::OutOfMemory();
- }
- // Allocate arena memory to store the string plus a terminating NUL
- char16* buffer = AnewArray(arena, char16, length + 1);
- js_wmemcpy_s(buffer, length + 1, content, length);
- buffer[length] = _u('\0');
- return buffer;
- }
- RecyclableObject * JavascriptString::CloneToScriptContext(ScriptContext* requestContext)
- {
- return JavascriptString::NewWithBuffer(this->GetSz(), this->GetLength(), requestContext);
- }
- charcount_t JavascriptString::ConvertToIndex(Var varIndex, ScriptContext *scriptContext)
- {
- if (TaggedInt::Is(varIndex))
- {
- return TaggedInt::ToInt32(varIndex);
- }
- return NumberUtilities::LwFromDblNearest(JavascriptConversion::ToInteger(varIndex, scriptContext));
- }
- char16* JavascriptString::GetNormalizedString(PlatformAgnostic::UnicodeText::NormalizationForm form, ArenaAllocator* tempAllocator, charcount_t& sizeOfNormalizedStringWithoutNullTerminator)
- {
- using namespace PlatformAgnostic;
- ScriptContext* scriptContext = this->GetScriptContext();
- if (this->GetLength() == 0)
- {
- sizeOfNormalizedStringWithoutNullTerminator = 0;
- return nullptr;
- }
- // IMPORTANT: Implementation Notes
- // Normalize string estimates the required size of the buffer based on averages and other data.
- // It is very hard to get a precise size from an input string without expanding/contracting it on the buffer.
- // It is estimated that the maximum size the string after an NFC is 6x the input length, and 18x for NFD. This approach isn't very feasible as well.
- // The approach taken is based on the simple example in the MSDN article.
- // - Loop until the return value is either an error (apart from insufficient buffer size), or success.
- // - Each time recreate a temporary buffer based on the last guess.
- // - When creating the JS string, use the positive return value and copy the buffer across.
- // Design choice for "guesses" comes from data Windows collected; and in most cases the loop will not iterate more than 2 times.
- Assert(!UnicodeText::IsNormalizedString(form, this->GetSz(), this->GetLength()));
- //Get the first size estimate
- UnicodeText::ApiError error;
- int32 sizeEstimate = UnicodeText::NormalizeString(form, this->GetSz(), this->GetLength() + 1, nullptr, 0, &error);
- char16 *tmpBuffer = nullptr;
- //Loop while the size estimate is bigger than 0
- while (error == UnicodeText::ApiError::InsufficientBuffer)
- {
- tmpBuffer = AnewArray(tempAllocator, char16, sizeEstimate);
- sizeEstimate = UnicodeText::NormalizeString(form, this->GetSz(), this->GetLength() + 1, tmpBuffer, sizeEstimate, &error);
- // Success, sizeEstimate is the exact size including the null terminator
- if (sizeEstimate > 0)
- {
- sizeOfNormalizedStringWithoutNullTerminator = sizeEstimate - 1;
- return tmpBuffer;
- }
- // Anything less than 0, we have an error, flip sizeEstimate now. As both times we need to use it, we need positive anyways.
- sizeEstimate *= -1;
- }
- switch (error)
- {
- case UnicodeText::ApiError::InvalidParameter:
- //some invalid parameter, coding error
- AssertMsg(false, "Invalid Parameter- check pointers passed to NormalizeString");
- JavascriptError::ThrowRangeError(scriptContext, JSERR_FailedToNormalize);
- break;
- case UnicodeText::ApiError::InvalidUnicodeText:
- //the value returned is the negative index of an invalid unicode character
- JavascriptError::ThrowRangeErrorVar(scriptContext, JSERR_InvalidUnicodeCharacter, sizeEstimate);
- break;
- case UnicodeText::ApiError::NoError:
- //The actual size of the output string is zero.
- //Theoretically only empty input string should produce this, which is handled above, thus the code path should not be hit.
- AssertMsg(false, "This code path should not be hit, empty string case is handled above. Perhaps a false error (sizeEstimate <= 0; but lastError == 0; ERROR_SUCCESS and NO_ERRROR == 0)");
- sizeOfNormalizedStringWithoutNullTerminator = 0;
- return nullptr; // scriptContext->GetLibrary()->GetEmptyString();
- break;
- default:
- AssertMsg(false, "Unknown error");
- JavascriptError::ThrowRangeError(scriptContext, JSERR_FailedToNormalize);
- break;
- }
- }
- void JavascriptString::InstantiateForceInlinedMembers()
- {
- // Force-inlined functions defined in a translation unit need a reference from an extern non-force-inlined function in
- // the same translation unit to force an instantiation of the force-inlined function. Otherwise, if the force-inlined
- // function is not referenced in the same translation unit, it will not be generated and the linker is not able to find
- // the definition to inline the function in other translation units.
- Assert(false);
- JavascriptString *const s = nullptr;
- s->ConcatDestructive(nullptr);
- }
- JavascriptString *
- JavascriptString::Concat3(JavascriptString * pstLeft, JavascriptString * pstCenter, JavascriptString * pstRight)
- {
- ConcatStringMulti * concatString = ConcatStringMulti::New(3, pstLeft, pstCenter, pstLeft->GetScriptContext());
- concatString->SetItem(2, pstRight);
- return concatString;
- }
- PropertyQueryFlags JavascriptString::HasPropertyQuery(PropertyId propertyId)
- {
- if (propertyId == PropertyIds::length)
- {
- return PropertyQueryFlags::Property_Found;
- }
- ScriptContext* scriptContext = GetScriptContext();
- charcount_t index;
- if (scriptContext->IsNumericPropertyId(propertyId, &index))
- {
- if (index < this->GetLength())
- {
- return PropertyQueryFlags::Property_Found;
- }
- }
- return PropertyQueryFlags::Property_NotFound;
- }
- BOOL JavascriptString::IsEnumerable(PropertyId propertyId)
- {
- ScriptContext* scriptContext = GetScriptContext();
- charcount_t index;
- if (scriptContext->IsNumericPropertyId(propertyId, &index))
- {
- if (index < this->GetLength())
- {
- return true;
- }
- }
- return false;
- }
- PropertyQueryFlags JavascriptString::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- return JavascriptConversion::BooleanToPropertyQueryFlags(GetPropertyBuiltIns(propertyId, value, requestContext));
- }
- PropertyQueryFlags JavascriptString::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- PropertyRecord const* propertyRecord;
- this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
- if (propertyRecord != nullptr && GetPropertyBuiltIns(propertyRecord->GetPropertyId(), value, requestContext))
- {
- return PropertyQueryFlags::Property_Found;
- }
- *value = requestContext->GetMissingPropertyResult();
- return PropertyQueryFlags::Property_NotFound;
- }
- bool JavascriptString::GetPropertyBuiltIns(PropertyId propertyId, Var* value, ScriptContext* requestContext)
- {
- if (propertyId == PropertyIds::length)
- {
- *value = JavascriptNumber::ToVar(this->GetLength(), requestContext);
- return true;
- }
- *value = requestContext->GetMissingPropertyResult();
- return false;
- }
- PropertyQueryFlags JavascriptString::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- return JavascriptString::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
- }
- BOOL JavascriptString::SetItem(uint32 index, Var value, PropertyOperationFlags propertyOperationFlags)
- {
- if (this->HasItemAt(index))
- {
- JavascriptError::ThrowCantAssignIfStrictMode(propertyOperationFlags, this->GetScriptContext());
- return FALSE;
- }
- return __super::SetItem(index, value, propertyOperationFlags);
- }
- BOOL JavascriptString::DeleteItem(uint32 index, PropertyOperationFlags propertyOperationFlags)
- {
- if (this->HasItemAt(index))
- {
- JavascriptError::ThrowCantDeleteIfStrictMode(propertyOperationFlags, this->GetScriptContext(), TaggedInt::ToString(index, this->GetScriptContext())->GetString());
- return FALSE;
- }
- return __super::DeleteItem(index, propertyOperationFlags);
- }
- PropertyQueryFlags JavascriptString::HasItemQuery(uint32 index)
- {
- return JavascriptConversion::BooleanToPropertyQueryFlags(this->HasItemAt(index));
- }
- PropertyQueryFlags JavascriptString::GetItemQuery(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
- {
- // String should always be marshalled to the current context
- Assert(requestContext == this->GetScriptContext());
- return JavascriptConversion::BooleanToPropertyQueryFlags(this->GetItemAt(index, value));
- }
- PropertyQueryFlags JavascriptString::GetItemReferenceQuery(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
- {
- // String should always be marshalled to the current context
- return JavascriptConversion::BooleanToPropertyQueryFlags(this->GetItemAt(index, value));
- }
- BOOL JavascriptString::GetEnumerator(JavascriptStaticEnumerator * enumerator, EnumeratorFlags flags, ScriptContext* requestContext, ForInCache * forInCache)
- {
- return enumerator->Initialize(
- RecyclerNew(GetScriptContext()->GetRecycler(), JavascriptStringEnumerator, this, requestContext),
- nullptr, nullptr, flags, requestContext, forInCache);
- }
- BOOL JavascriptString::DeleteProperty(PropertyId propertyId, PropertyOperationFlags propertyOperationFlags)
- {
- if (propertyId == PropertyIds::length)
- {
- JavascriptError::ThrowCantDeleteIfStrictMode(propertyOperationFlags, this->GetScriptContext(), this->GetScriptContext()->GetPropertyName(propertyId)->GetBuffer());
- return FALSE;
- }
- return __super::DeleteProperty(propertyId, propertyOperationFlags);
- }
- BOOL JavascriptString::DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags propertyOperationFlags)
- {
- if (BuiltInPropertyRecords::length.Equals(propertyNameString))
- {
- JavascriptError::ThrowCantDeleteIfStrictMode(propertyOperationFlags, this->GetScriptContext(), propertyNameString->GetString());
- return FALSE;
- }
- return __super::DeleteProperty(propertyNameString, propertyOperationFlags);
- }
- BOOL JavascriptString::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
- {
- stringBuilder->Append(_u('"'));
- stringBuilder->Append(this->GetString(), this->GetLength());
- stringBuilder->Append(_u('"'));
- return TRUE;
- }
- BOOL JavascriptString::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
- {
- stringBuilder->AppendCppLiteral(_u("String"));
- return TRUE;
- }
- RecyclableObject* JavascriptString::ToObject(ScriptContext * requestContext)
- {
- return requestContext->GetLibrary()->CreateStringObject(this);
- }
- Var JavascriptString::GetTypeOfString(ScriptContext * requestContext)
- {
- return requestContext->GetLibrary()->GetStringTypeDisplayString();
- }
- /* static */
- template <typename T>
- bool JavascriptStringHelpers<T>::Equals(Var aLeft, Var aRight)
- {
- AssertMsg(T::Is(aLeft) && T::Is(aRight), "string comparison");
- if (aLeft == aRight) return true;
- T *leftString = T::UnsafeFromVar(aLeft);
- T *rightString = T::UnsafeFromVar(aRight);
- // methods could get called a lot of times this can show up as regressions in benchmarks.
- volatile T** keepAliveLeftString = (volatile T**)& leftString;
- volatile T** keepAliveRightString = (volatile T**)& rightString;
- auto keepAliveLambda = [&]() {
- UNREFERENCED_PARAMETER(keepAliveLeftString);
- UNREFERENCED_PARAMETER(keepAliveRightString);
- };
- if (leftString->GetLength() != rightString->GetLength())
- {
- return false;
- }
- if (wmemcmp(leftString->GetString(), rightString->GetString(), leftString->GetLength()) == 0)
- {
- return true;
- }
- return false;
- }
- #if ENABLE_NATIVE_CODEGEN
- template bool JavascriptStringHelpers<JITJavascriptString>::Equals(Var aLeft, Var aRight);
- #endif
- }
|