| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921 |
- //-------------------------------------------------------------------------------------------------------
- // 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;
- }
- void JavascriptString::GetPropertyRecord(_Out_ Js::PropertyRecord const ** propertyRecord, bool dontLookupFromDictionary)
- {
- *propertyRecord = nullptr;
- if (dontLookupFromDictionary)
- {
- return;
- }
- GetScriptContext()->GetOrAddPropertyRecord(GetString(), GetLength(), propertyRecord);
- }
- void JavascriptString::CachePropertyRecord(_In_ PropertyRecord const* propertyRecord)
- {
- // Base string doesn't have enough room to keep this value, so do nothing
- }
- 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(TARGET_64)
- 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, ...)
- {
- using namespace PlatformAgnostic;
- 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));
- #ifdef INTL_WINGLOB
- if (args.Info.Count == 2)
- {
- auto undefined = scriptContext->GetLibrary()->GetUndefined();
- CallInfo toPass(callInfo.Flags, 3);
- 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);
- }
- );
- }
- #endif
- // Check if String.prototype.localeCompare/Intl.Collator was already initialized
- JavascriptFunction* func = intlExtensionObject->GetStringLocaleCompare();
- if (func)
- {
- BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
- {
- return func->CallFunction(args);
- }
- END_SAFE_REENTRANT_CALL
- }
- // String.prototype.localeCompare/Intl.Collator was not initialized yet, so we need to manually initialize it here
- scriptContext->GetLibrary()->InitializeIntlForStringPrototype();
- func = intlExtensionObject->GetStringLocaleCompare();
- if (func)
- {
- BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
- {
- return func->CallFunction(args);
- }
- END_SAFE_REENTRANT_CALL
- }
- }
- }
- #endif
- const char16* pThisStr = pThis->GetString();
- int thisStrCount = pThis->GetLength();
- const char16* pThatStr = pThat->GetString();
- int thatStrCount = pThat->GetLength();
- int result = UnicodeText::LogicalStringCompare(pThisStr, thisStrCount, pThatStr, thatStrCount);
- // LogicalStringCompare will return -2 if CompareStringEx fails.
- if (result == -2)
- {
- // 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, scriptContext);
- }
- 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
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- return threadContext->ExecuteImplicitCall(fnObj, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, 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);
- }
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- return threadContext->ExecuteImplicitCall(fnObj, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, 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));
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.toLocaleLowerCase"), &pThis);
- return ToLocaleCaseHelper<false /* toUpper */>(pThis);
- }
- 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));
- JavascriptString * pThis = nullptr;
- GetThisStringArgument(args, scriptContext, _u("String.prototype.toLocaleUpperCase"), &pThis);
- return ToLocaleCaseHelper<true /* toUpper */>(pThis);
- }
- template<bool toUpper>
- JavascriptString* JavascriptString::ToLocaleCaseHelper(JavascriptString* pThis)
- {
- // TODO: implement locale-sensitive Intl versions of these functions
- return ToCaseCore<toUpper, false>(pThis);
- }
- 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);
- return ToCaseCore<false, true>(pThis);
- }
- 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);
- return ToCaseCore<true, true>(pThis);
- }
- template<bool toUpper, bool useInvariant>
- JavascriptString* JavascriptString::ToCaseCore(JavascriptString* pThis)
- {
- using namespace PlatformAgnostic::UnicodeText;
- if (pThis->GetLength() == 0)
- {
- return pThis;
- }
- ScriptContext* scriptContext = pThis->type->GetScriptContext();
- ApiError error = ApiError::NoError;
- const char16 *pThisSz = pThis->GetSz();
- charcount_t pThisLength = pThis->GetLength();
- if (useInvariant)
- {
- bool isAscii = true;
- for (charcount_t i = 0; i < pThisLength; i++)
- {
- if (pThisSz[i] >= 0x80)
- {
- isAscii = false;
- break;
- }
- }
- if (isAscii)
- {
- char16 *ret = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), char16, UInt32Math::Add(pThisLength, 1));
- const char16 diffBetweenCases = 32;
- for (charcount_t i = 0; i < pThisLength; i++)
- {
- char16 cur = pThisSz[i];
- if (toUpper)
- {
- if (cur >= _u('a') && cur <= _u('z'))
- {
- ret[i] = cur - diffBetweenCases;
- }
- else
- {
- ret[i] = cur;
- }
- }
- else
- {
- if (cur >= _u('A') && cur <= _u('Z'))
- {
- ret[i] = cur + diffBetweenCases;
- }
- else
- {
- ret[i] = cur;
- }
- }
- }
- ret[pThisLength] = 0;
- return JavascriptString::NewWithBuffer(ret, pThisLength, scriptContext);
- }
- }
- // pre-flight to get the length required, as it may be longer than the original string
- // ICU and Win32(/POSIX) implementations of these functions differ slightly in how to get the required number of characters.
- // For Win32 (LCMapStringEx), you must provide nullptr/0, as providing a buffer that is too small will cause an error and will *not*
- // report the number of characters required. For ICU, however, you can provide a buffer that is too short, and it will still return
- // the length it actually needs.
- //
- // This is a small performance optimization because to(Upper|Lower)Case is can show up hot in certain scenarios.
- // ICU still allows nullptr/0 to be passed to get the string length, and more conservative callers of ChangeStringLinguisticCase should do just that.
- // TODO(jahorto): A truly PlatformAgnostic API wouldn't require cases like this. Once PlatformAgnostic is allowed to use
- // Chakra's memory subsystems, this API should be converted to one that only takes a source string and returns a Recycler-allocated
- // string in the correct case, performed using whatever operation is the fastest available on that platform.
- #ifdef INTL_ICU
- charcount_t guessBufferLength = UInt32Math::Add(pThisLength, 1);
- char16 *guessBuffer = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), char16, guessBufferLength);
- #else
- charcount_t guessBufferLength = 0;
- char16 *guessBuffer = nullptr;
- #endif
- charcount_t requiredStringLength = ChangeStringLinguisticCase<toUpper, useInvariant>(pThis->GetSz(), pThis->GetLength(), guessBuffer, guessBufferLength, &error);
- if (error == ApiError::OutOfMemory)
- {
- Throw::OutOfMemory();
- }
- // We exit ToCaseCore early if the source string is 0-length, and casing a non-zero length string should
- // never result in a zero-length string.
- AssertOrFailFast(requiredStringLength > 0 && IsValidCharCount(requiredStringLength));
- #ifdef INTL_ICU
- if (error == ApiError::NoError)
- {
- if (requiredStringLength == 1)
- {
- // don't create a new string in case we may have cached this string earlier
- return scriptContext->GetLibrary()->GetCharStringCache().GetStringForChar(guessBuffer[0]);
- }
- else
- {
- // use requiredStringLength instead of guessBufferLength because the string can get shorter
- return JavascriptString::NewWithBuffer(guessBuffer, requiredStringLength, scriptContext);
- }
- }
- AssertOrFailFast(error == ApiError::InsufficientBuffer);
- #else
- AssertOrFailFast(error == ApiError::NoError);
- if (requiredStringLength == 1)
- {
- // this one-char string special case is only for non-ICU because there should never be a case where the error
- // was InsufficientBufer but the required length was 1
- char16 buffer[2] = { pThis->GetSz()[0], 0 };
- charcount_t actualStringLength = ChangeStringLinguisticCase<toUpper, useInvariant>(pThis->GetSz(), pThis->GetLength(), buffer, 2, &error);
- AssertOrFailFast(actualStringLength == 1 && error == ApiError::NoError);
- return scriptContext->GetLibrary()->GetCharStringCache().GetStringForChar(buffer[0]);
- }
- #endif
- AssertOrFailFast(requiredStringLength > 1);
- charcount_t bufferLength = UInt32Math::Add(requiredStringLength, 1);
- char16* buffer = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), char16, bufferLength);
- charcount_t actualStringLength = ChangeStringLinguisticCase<toUpper, useInvariant>(pThis->GetSz(), pThis->GetLength(), buffer, bufferLength, &error);
- AssertOrFailFast(actualStringLength == requiredStringLength && error == ApiError::NoError);
- return JavascriptString::NewWithBuffer(buffer, actualStringLength, scriptContext);
- }
- 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 (pch == this->m_pszValue + len)
- {
- *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(JavascriptString* aLeft, JavascriptString* 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();
- }
- 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, _Inout_opt_ PropertyValueInfo* info)
- {
- 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, EnumeratorCache * enumeratorCache)
- {
- return enumerator->Initialize(
- RecyclerNew(GetScriptContext()->GetRecycler(), JavascriptStringEnumerator, this, requestContext),
- nullptr, nullptr, flags, requestContext, enumeratorCache);
- }
- 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(T* aLeft, T* aRight)
- {
- if (aLeft == aRight) return true;
- // methods could get called a lot of times this can show up as regressions in benchmarks.
- volatile T** keepAliveLeftString = (volatile T**)& aLeft;
- volatile T** keepAliveRightString = (volatile T**)& aRight;
- auto keepAliveLambda = [&]() {
- UNREFERENCED_PARAMETER(keepAliveLeftString);
- UNREFERENCED_PARAMETER(keepAliveRightString);
- };
- if (aLeft->GetLength() != aRight->GetLength())
- {
- return false;
- }
- return JsUtil::CharacterBuffer<char16>::StaticEquals(aLeft->GetString(), aRight->GetString(), aLeft->GetLength());
- }
- #if ENABLE_NATIVE_CODEGEN
- template bool JavascriptStringHelpers<JITJavascriptString>::Equals(JITJavascriptString* aLeft, JITJavascriptString* aRight);
- #endif
- }
|