| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448 |
- //-------------------------------------------------------------------------------------------------------
- // 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 "Library/StackScriptFunction.h"
- #include "Types/SpreadArgument.h"
- #include "Language/AsmJsTypes.h"
- #ifdef _M_X64
- #include "ByteCode/PropertyIdArray.h"
- #include "Language/AsmJsModule.h"
- #endif
- #ifdef _M_IX86
- #ifdef _CONTROL_FLOW_GUARD
- extern "C" PVOID __guard_check_icall_fptr;
- #endif
- extern "C" void __cdecl _alloca_probe_16();
- #endif
- namespace Js
- {
- // The VS2013 linker treats this as a redefinition of an already
- // defined constant and complains. So skip the declaration if we're compiling
- // with VS2013 or below.
- #if !defined(_MSC_VER) || _MSC_VER >= 1900
- const charcount_t JavascriptFunction::DIAG_MAX_FUNCTION_STRING;
- #endif
- DEFINE_RECYCLER_TRACKER_PERF_COUNTER(JavascriptFunction);
- JavascriptFunction::JavascriptFunction(DynamicType * type)
- : DynamicObject(type), functionInfo(nullptr), constructorCache(&ConstructorCache::DefaultInstance)
- {
- Assert(this->constructorCache != nullptr);
- }
- JavascriptFunction::JavascriptFunction(DynamicType * type, FunctionInfo * functionInfo)
- : DynamicObject(type), functionInfo(functionInfo), constructorCache(&ConstructorCache::DefaultInstance)
- {
- Assert(this->constructorCache != nullptr);
- this->GetTypeHandler()->ClearHasOnlyWritableDataProperties(); // length is non-writable
- if (GetTypeHandler()->GetFlags() & DynamicTypeHandler::IsPrototypeFlag)
- {
- // No need to invalidate store field caches for non-writable properties here. Since this type is just being created, it cannot represent
- // an object that is already a prototype. If it becomes a prototype and then we attempt to add a property to an object derived from this
- // object, then we will check if this property is writable, and only if it is will we do the fast path for add property.
- // GetScriptContext()->InvalidateStoreFieldCaches(PropertyIds::length);
- GetLibrary()->NoPrototypeChainsAreEnsuredToHaveOnlyWritableDataProperties();
- }
- }
- JavascriptFunction::JavascriptFunction(DynamicType * type, FunctionInfo * functionInfo, ConstructorCache* cache)
- : DynamicObject(type), functionInfo(functionInfo), constructorCache(cache)
- {
- Assert(this->constructorCache != nullptr);
- this->GetTypeHandler()->ClearHasOnlyWritableDataProperties(); // length is non-writable
- if (GetTypeHandler()->GetFlags() & DynamicTypeHandler::IsPrototypeFlag)
- {
- // No need to invalidate store field caches for non-writable properties here. Since this type is just being created, it cannot represent
- // an object that is already a prototype. If it becomes a prototype and then we attempt to add a property to an object derived from this
- // object, then we will check if this property is writable, and only if it is will we do the fast path for add property.
- // GetScriptContext()->InvalidateStoreFieldCaches(PropertyIds::length);
- GetLibrary()->NoPrototypeChainsAreEnsuredToHaveOnlyWritableDataProperties();
- }
- }
- FunctionProxy *JavascriptFunction::GetFunctionProxy() const
- {
- Assert(functionInfo != nullptr);
- return functionInfo->GetFunctionProxy();
- }
- ParseableFunctionInfo *JavascriptFunction::GetParseableFunctionInfo() const
- {
- Assert(functionInfo != nullptr);
- return functionInfo->GetParseableFunctionInfo();
- }
- DeferDeserializeFunctionInfo *JavascriptFunction::GetDeferDeserializeFunctionInfo() const
- {
- Assert(functionInfo != nullptr);
- return functionInfo->GetDeferDeserializeFunctionInfo();
- }
- FunctionBody *JavascriptFunction::GetFunctionBody() const
- {
- Assert(functionInfo != nullptr);
- return functionInfo->GetFunctionBody();
- }
- BOOL JavascriptFunction::IsScriptFunction() const
- {
- Assert(functionInfo != nullptr);
- return functionInfo->HasBody();
- }
- bool JavascriptFunction::Is(Var aValue)
- {
- if (JavascriptOperators::GetTypeId(aValue) == TypeIds_Function)
- {
- return true;
- }
- return false;
- }
- JavascriptFunction* JavascriptFunction::FromVar(Var aValue)
- {
- AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptFunction'");
- return static_cast<JavascriptFunction *>(RecyclableObject::FromVar(aValue));
- }
- BOOL JavascriptFunction::IsStrictMode() const
- {
- FunctionProxy * proxy = this->GetFunctionProxy();
- return proxy && proxy->EnsureDeserialized()->GetIsStrictMode();
- }
- BOOL JavascriptFunction::IsLambda() const
- {
- return this->GetFunctionInfo()->IsLambda();
- }
- BOOL JavascriptFunction::IsConstructor() const
- {
- return this->GetFunctionInfo()->IsConstructor();
- }
- #if DBG
- /* static */
- bool JavascriptFunction::IsBuiltinProperty(Var objectWithProperty, PropertyIds propertyId)
- {
- return ScriptFunctionBase::Is(objectWithProperty)
- && (propertyId == PropertyIds::length || (JavascriptFunction::FromVar(objectWithProperty)->HasRestrictedProperties() && (propertyId == PropertyIds::arguments || propertyId == PropertyIds::caller)));
- }
- #endif
- Var JavascriptFunction::NewInstanceHelper(ScriptContext *scriptContext, RecyclableObject* function, CallInfo callInfo, Js::ArgumentReader& args, FunctionKind functionKind /* = FunctionKind::Normal */)
- {
- JavascriptLibrary* library = function->GetLibrary();
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- // SkipDefaultNewObject function flag should have prevented the default object from
- // being created, except when call true a host dispatch.
- Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0];
- bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget);
- Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr
- || JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch);
- JavascriptString* separator = library->GetCommaDisplayString();
- // Gather all the formals into a string like (fml1, fml2, fml3)
- JavascriptString *formals = library->GetOpenRBracketString();
- for (uint i = 1; i < args.Info.Count - 1; ++i)
- {
- if (i != 1)
- {
- formals = JavascriptString::Concat(formals, separator);
- }
- formals = JavascriptString::Concat(formals, JavascriptConversion::ToString(args.Values[i], scriptContext));
- }
- formals = JavascriptString::Concat(formals, library->GetNewLineCloseRBracketString());
- // Function body, last argument to Function(...)
- JavascriptString *fnBody = NULL;
- if (args.Info.Count > 1)
- {
- fnBody = JavascriptConversion::ToString(args.Values[args.Info.Count - 1], scriptContext);
- }
- // Create a string representing the anonymous function
- Assert(
- 0 + // "function anonymous" GetFunctionAnonymousString
- 0 + // "(" GetOpenRBracketString
- 1 + // "\n)" GetNewLineCloseRBracketString
- 0 // " {" GetSpaceOpenBracketString
- == numberLinesPrependedToAnonymousFunction); // Be sure to add exactly one line to anonymous function
- JavascriptString *bs = functionKind == FunctionKind::Async ?
- library->GetAsyncFunctionAnonymouseString() :
- functionKind == FunctionKind::Generator ?
- library->GetFunctionPTRAnonymousString() :
- library->GetFunctionAnonymousString();
- bs = JavascriptString::Concat(bs, formals);
- bs = JavascriptString::Concat(bs, library->GetSpaceOpenBracketString());
- if (fnBody != NULL)
- {
- bs = JavascriptString::Concat(bs, fnBody);
- }
- bs = JavascriptString::Concat(bs, library->GetNewLineCloseBracketString());
- // Bug 1105479. Get the module id from the caller
- ModuleID moduleID = kmodGlobal;
- BOOL strictMode = FALSE;
- JavascriptFunction *pfuncScript;
- FunctionInfo *pfuncInfoCache = NULL;
- char16 const * sourceString = bs->GetSz();
- charcount_t sourceLen = bs->GetLength();
- EvalMapString key(sourceString, sourceLen, moduleID, strictMode, /* isLibraryCode = */ false);
- if (!scriptContext->IsInNewFunctionMap(key, &pfuncInfoCache))
- {
- // Validate formals here
- scriptContext->GetGlobalObject()->ValidateSyntax(
- scriptContext, formals->GetSz(), formals->GetLength(),
- functionKind == FunctionKind::Generator, functionKind == FunctionKind::Async,
- &Parser::ValidateFormals);
- if (fnBody != NULL)
- {
- // Validate function body
- scriptContext->GetGlobalObject()->ValidateSyntax(
- scriptContext, fnBody->GetSz(), fnBody->GetLength(),
- functionKind == FunctionKind::Generator, functionKind == FunctionKind::Async,
- &Parser::ValidateSourceElementList);
- }
- pfuncScript = scriptContext->GetGlobalObject()->EvalHelper(scriptContext, sourceString, sourceLen, moduleID, fscrNil, Constants::FunctionCode, TRUE, TRUE, strictMode);
- // Indicate that this is a top-level function. We don't pass the fscrGlobalCode flag to the eval helper,
- // or it will return the global function that wraps the declared function body, as though it were an eval.
- // But we want, for instance, to be able to verify that we did the right amount of deferred parsing.
- ParseableFunctionInfo *functionInfo = pfuncScript->GetParseableFunctionInfo();
- Assert(functionInfo);
- functionInfo->SetGrfscr(functionInfo->GetGrfscr() | fscrGlobalCode);
- #if ENABLE_TTD
- if(!scriptContext->IsTTDRecordOrReplayModeEnabled())
- {
- scriptContext->AddToNewFunctionMap(key, functionInfo->GetFunctionInfo());
- }
- #else
- scriptContext->AddToNewFunctionMap(key, functionInfo->GetFunctionInfo());
- #endif
- }
- else if (pfuncInfoCache->IsCoroutine())
- {
- pfuncScript = scriptContext->GetLibrary()->CreateGeneratorVirtualScriptFunction(pfuncInfoCache->GetFunctionProxy());
- }
- else
- {
- pfuncScript = scriptContext->GetLibrary()->CreateScriptFunction(pfuncInfoCache->GetFunctionProxy());
- }
- #if ENABLE_TTD
- //
- //TODO: We may (probably?) want to use the debugger source rundown functionality here instead
- //
- if(pfuncScript != nullptr && (scriptContext->IsTTDRecordModeEnabled() || scriptContext->ShouldPerformReplayAction()))
- {
- //Make sure we have the body and text information available
- FunctionBody* globalBody = TTD::JsSupport::ForceAndGetFunctionBody(pfuncScript->GetParseableFunctionInfo());
- if(!scriptContext->TTDContextInfo->IsBodyAlreadyLoadedAtTopLevel(globalBody))
- {
- uint32 bodyIdCtr = 0;
- if(scriptContext->IsTTDRecordModeEnabled())
- {
- const TTD::NSSnapValues::TopLevelNewFunctionBodyResolveInfo* tbfi = scriptContext->GetThreadContext()->TTDLog->AddNewFunction(globalBody, moduleID, sourceString, sourceLen);
- //We always want to register the top-level load but we don't always need to log the event
- if(scriptContext->ShouldPerformRecordAction())
- {
- scriptContext->GetThreadContext()->TTDLog->RecordTopLevelCodeAction(tbfi->TopLevelBase.TopLevelBodyCtr);
- }
- bodyIdCtr = tbfi->TopLevelBase.TopLevelBodyCtr;
- }
- if(scriptContext->ShouldPerformReplayAction())
- {
- bodyIdCtr = scriptContext->GetThreadContext()->TTDLog->ReplayTopLevelCodeAction();
- }
- //walk global body to (1) add functions to pin set (2) build parent map
- scriptContext->TTDContextInfo->ProcessFunctionBodyOnLoad(globalBody, nullptr);
- scriptContext->TTDContextInfo->RegisterNewScript(globalBody, bodyIdCtr);
- if(scriptContext->ShouldPerformRecordOrReplayAction())
- {
- globalBody->GetUtf8SourceInfo()->SetSourceInfoForDebugReplay_TTD(bodyIdCtr);
- }
- if(scriptContext->ShouldPerformDebuggerAction())
- {
- scriptContext->GetThreadContext()->TTDExecutionInfo->ProcessScriptLoad(scriptContext, bodyIdCtr, globalBody, globalBody->GetUtf8SourceInfo(), nullptr);
- }
- }
- }
- #endif
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_FUNCTION(pfuncScript, EtwTrace::GetFunctionId(pfuncScript->GetFunctionProxy())));
- if (functionKind == FunctionKind::Generator || functionKind == FunctionKind::Async)
- {
- Assert(pfuncScript->GetFunctionInfo()->IsCoroutine());
- auto pfuncVirt = static_cast<GeneratorVirtualScriptFunction*>(pfuncScript);
- auto pfuncGen = functionKind == FunctionKind::Async ?
- scriptContext->GetLibrary()->CreateAsyncFunction(JavascriptAsyncFunction::EntryAsyncFunctionImplementation, pfuncVirt) :
- scriptContext->GetLibrary()->CreateGeneratorFunction(JavascriptGeneratorFunction::EntryGeneratorFunctionImplementation, pfuncVirt);
- pfuncVirt->SetRealGeneratorFunction(pfuncGen);
- pfuncScript = pfuncGen;
- }
- return isCtorSuperCall ?
- JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), pfuncScript, nullptr, scriptContext) :
- pfuncScript;
- }
- Var JavascriptFunction::NewInstanceRestrictedMode(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ScriptContext* scriptContext = function->GetScriptContext();
- scriptContext->CheckEvalRestriction();
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- return NewInstanceHelper(scriptContext, function, callInfo, args);
- }
- Var JavascriptFunction::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- return NewInstanceHelper(scriptContext, function, callInfo, args);
- }
- Var JavascriptFunction::NewAsyncFunctionInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- // Get called when creating a new async function through the constructor (e.g. af.__proto__.constructor)
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- return JavascriptFunction::NewInstanceHelper(function->GetScriptContext(), function, callInfo, args, JavascriptFunction::FunctionKind::Async);
- }
- Var JavascriptFunction::NewAsyncFunctionInstanceRestrictedMode(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ScriptContext* scriptContext = function->GetScriptContext();
- scriptContext->CheckEvalRestriction();
- PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- return JavascriptFunction::NewInstanceHelper(scriptContext, function, callInfo, args, JavascriptFunction::FunctionKind::Async);
- }
- //
- // Dummy EntryPoint for Function.prototype
- //
- Var JavascriptFunction::PrototypeEntryPoint(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- JavascriptLibrary* library = function->GetLibrary();
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- if (callInfo.Flags & CallFlags_New)
- {
- JavascriptError::ThrowTypeError(scriptContext, VBSERR_ActionNotSupported);
- }
- return library->GetUndefined();
- }
- enum : unsigned { STACK_ARGS_ALLOCA_THRESHOLD = 8 }; // Number of stack args we allow before using _alloca
- // ES5 15.3.4.3
- //When the apply method is called on an object func with arguments thisArg and argArray the following steps are taken:
- // 1. If IsCallable(func) is false, then throw a TypeError exception.
- // 2. If argArray is null or undefined, then
- // a. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and an empty list of arguments.
- // 3. If Type(argArray) is not Object, then throw a TypeError exception.
- // 4. Let len be the result of calling the [[Get]] internal method of argArray with argument "length".
- //
- // Steps 5 and 7 deleted from July 19 Errata of ES5 spec
- //
- // 5. If len is null or undefined, then throw a TypeError exception.
- // 6. Len n be ToUint32(len).
- // 7. If n is not equal to ToNumber(len), then throw a TypeError exception.
- // 8. Let argList be an empty List.
- // 9. Let index be 0.
- // 10. Repeat while index < n
- // a. Let indexName be ToString(index).
- // b. Let nextArg be the result of calling the [[Get]] internal method of argArray with indexName as the argument.
- // c. Append nextArg as the last element of argList.
- // d. Set index to index + 1.
- // 11. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and argList as the list of arguments.
- // The length property of the apply method is 2.
- Var JavascriptFunction::EntryApply(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- // Ideally, we want to maintain CallFlags_Eval behavior and pass along the extra FrameDisplay parameter
- // but that we would be a bigger change than what we want to do in this ship cycle. See WIN8: 915315.
- // If eval is executed using apply it will not get the frame display and always execute in global scope.
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- ///
- /// Check Argument[0] has internal [[Call]] property
- /// If not, throw TypeError
- ///
- if (args.Info.Count == 0 || !JavascriptConversion::IsCallable(args[0]))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedFunction, _u("Function.prototype.apply"));
- }
- Var thisVar = NULL;
- Var argArray = NULL;
- RecyclableObject* pFunc = RecyclableObject::FromVar(args[0]);
- if (args.Info.Count == 1)
- {
- thisVar = scriptContext->GetLibrary()->GetUndefined();
- }
- else if (args.Info.Count == 2)
- {
- thisVar = args.Values[1];
- }
- else if (args.Info.Count > 2)
- {
- thisVar = args.Values[1];
- argArray = args.Values[2];
- }
- return CalloutHelper<false>(pFunc, thisVar, /* overridingNewTarget = */nullptr, argArray, scriptContext);
- }
- template <bool isConstruct>
- Var JavascriptFunction::CalloutHelper(RecyclableObject* pFunc, Var thisVar, Var overridingNewTarget, Var argArray, ScriptContext* scriptContext)
- {
- CallFlags callFlag;
- if (isConstruct)
- {
- callFlag = CallFlags_New;
- }
- else
- {
- callFlag = CallFlags_Value;
- }
- Arguments outArgs(CallInfo(callFlag, 0), nullptr);
- Var stackArgs[STACK_ARGS_ALLOCA_THRESHOLD];
- if (nullptr == argArray)
- {
- outArgs.Info.Count = 1;
- outArgs.Values = &thisVar;
- }
- else
- {
- bool isArray = JavascriptArray::Is(argArray);
- TypeId typeId = JavascriptOperators::GetTypeId(argArray);
- bool isNullOrUndefined = (typeId == TypeIds_Null || typeId == TypeIds_Undefined);
- if (!isNullOrUndefined && !JavascriptOperators::IsObject(argArray)) // ES5: throw if Type(argArray) is not Object
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Function.prototype.apply"));
- }
- int64 len;
- JavascriptArray* arr = NULL;
- RecyclableObject* dynamicObject = RecyclableObject::FromVar(argArray);
- if (isNullOrUndefined)
- {
- len = 0;
- }
- else if (isArray)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(argArray);
- #endif
- arr = JavascriptArray::FromVar(argArray);
- len = arr->GetLength();
- }
- else
- {
- Var lenProp = JavascriptOperators::OP_GetLength(dynamicObject, scriptContext);
- len = JavascriptConversion::ToLength(lenProp, scriptContext);
- }
- if (len >= CallInfo::kMaxCountArgs)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgListTooLarge);
- }
- outArgs.Info.Count = (uint)len + 1;
- if (len == 0)
- {
- outArgs.Values = &thisVar;
- }
- else
- {
- if (outArgs.Info.Count > STACK_ARGS_ALLOCA_THRESHOLD)
- {
- PROBE_STACK(scriptContext, outArgs.Info.Count * sizeof(Var)+Js::Constants::MinStackDefault); // args + function call
- outArgs.Values = (Var*)_alloca(outArgs.Info.Count * sizeof(Var));
- }
- else
- {
- outArgs.Values = stackArgs;
- }
- outArgs.Values[0] = thisVar;
- Var undefined = pFunc->GetLibrary()->GetUndefined();
- if (isArray && arr->GetScriptContext() == scriptContext)
- {
- arr->ForEachItemInRange<false>(0, (uint)len, undefined, scriptContext,
- [&outArgs](uint index, Var element)
- {
- outArgs.Values[index + 1] = element;
- });
- }
- else
- {
- for (uint i = 0; i < len; i++)
- {
- Var element = nullptr;
- if (!JavascriptOperators::GetItem(dynamicObject, i, &element, scriptContext))
- {
- element = undefined;
- }
- outArgs.Values[i + 1] = element;
- }
- }
- }
- }
- if (isConstruct)
- {
- return JavascriptFunction::CallAsConstructor(pFunc, overridingNewTarget, outArgs, scriptContext);
- }
- else
- {
- return JavascriptFunction::CallFunction<true>(pFunc, pFunc->GetEntryPoint(), outArgs);
- }
- }
- Var JavascriptFunction::ApplyHelper(RecyclableObject* function, Var thisArg, Var argArray, ScriptContext* scriptContext)
- {
- return CalloutHelper<false>(function, thisArg, /* overridingNewTarget = */nullptr, argArray, scriptContext);
- }
- Var JavascriptFunction::ConstructHelper(RecyclableObject* function, Var thisArg, Var overridingNewTarget, Var argArray, ScriptContext* scriptContext)
- {
- return CalloutHelper<true>(function, thisArg, overridingNewTarget, argArray, scriptContext);
- }
- Var JavascriptFunction::EntryBind(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Function_Prototype_bind);
- Assert(!(callInfo.Flags & CallFlags_New));
- ///
- /// Check Argument[0] has internal [[Call]] property
- /// If not, throw TypeError
- ///
- if (args.Info.Count == 0 || !JavascriptConversion::IsCallable(args[0]))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedFunction, _u("Function.prototype.bind"));
- }
- BoundFunction* boundFunc = BoundFunction::New(scriptContext, args);
- return boundFunc;
- }
- // ES5 15.3.4.4
- // Function.prototype.call (thisArg [ , arg1 [ , arg2, ... ] ] )
- // When the call method is called on an object func with argument thisArg and optional arguments arg1, arg2 etc, the following steps are taken:
- // 1. If IsCallable(func) is false, then throw a TypeError exception.
- // 2. Let argList be an empty List.
- // 3. If this method was called with more than one argument then in left to right order starting with arg1 append each argument as the last element of argList
- // 4. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and argList as the list of arguments.
- // The length property of the call method is 1.
- Var JavascriptFunction::EntryCall(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- RUNTIME_ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- ///
- /// Check Argument[0] has internal [[Call]] property
- /// If not, throw TypeError
- ///
- uint argCount = args.Info.Count;
- if (callInfo.Flags & CallFlags_ExtraArg)
- {
- // The last argument is the "extra". Don't consider it in the logic below.
- // It will either remain in place (argCount == 1) or be copied.
- argCount--;
- }
- if (argCount == 0 || !JavascriptConversion::IsCallable(args[0]))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedFunction, _u("Function.prototype.call"));
- }
- RecyclableObject *pFunc = RecyclableObject::FromVar(args[0]);
- if (argCount == 1)
- {
- args.Values[0] = scriptContext->GetLibrary()->GetUndefined();
- }
- else
- {
- ///
- /// Remove function object from the arguments and pass the rest
- ///
- for (uint i = 0; i < args.Info.Count - 1; ++i)
- {
- args.Values[i] = args.Values[i + 1];
- }
- args.Info.Count = args.Info.Count - 1;
- }
- ///
- /// Call the [[Call]] method on the function object
- ///
- return JavascriptFunction::CallFunction<true>(pFunc, pFunc->GetEntryPoint(), args);
- }
- Var JavascriptFunction::CallRootFunctionInScript(JavascriptFunction* func, Arguments args)
- {
- ScriptContext* scriptContext = func->GetScriptContext();
- if (scriptContext->GetThreadContext()->HasPreviousHostScriptContext())
- {
- ScriptContext* requestContext = scriptContext->GetThreadContext()->GetPreviousHostScriptContext()->GetScriptContext();
- func = JavascriptFunction::FromVar(CrossSite::MarshalVar(requestContext, func));
- }
- return func->CallRootFunction(args, scriptContext, true);
- }
- Var JavascriptFunction::CallRootFunction(RecyclableObject* obj, Arguments args, ScriptContext * scriptContext, bool inScript)
- {
- Var ret = nullptr;
- #ifdef FAULT_INJECTION
- if (Js::Configuration::Global.flags.FaultInjection >= 0)
- {
- Js::FaultInjection::pfnHandleAV = JavascriptFunction::CallRootEventFilter;
- __try
- {
- ret = JavascriptFunction::CallRootFunctionInternal(obj, args, scriptContext, inScript);
- }
- __finally
- {
- Js::FaultInjection::pfnHandleAV = nullptr;
- }
- //ret should never be null here
- Assert(ret);
- return ret;
- }
- #endif
- #ifdef DISABLE_SEH
- // xplat: JavascriptArrayBuffer::AllocWrapper is disabled on cross-platform
- // (IsValidVirtualBufferLength always returns false).
- // SEH and ResumeForOutOfBoundsArrayRefs are not needed.
- ret = JavascriptFunction::CallRootFunctionInternal(obj, args, scriptContext, inScript);
- #else
- if (scriptContext->GetThreadContext()->GetAbnormalExceptionCode() != 0)
- {
- // ensure that hosts are not doing SEH across Chakra frames, as that can lead to bad state (e.g. destructors not being called)
- UnexpectedExceptionHandling_fatal_error();
- }
- // mark volatile, because otherwise VC will incorrectly optimize away load in the finally block
- volatile uint32 exceptionCode = 0;
- EXCEPTION_POINTERS exceptionInfo = { 0 };
- __try
- {
- __try
- {
- ret = JavascriptFunction::CallRootFunctionInternal(obj, args, scriptContext, inScript);
- }
- __except (
- exceptionInfo = *GetExceptionInformation(),
- exceptionCode = GetExceptionCode(),
- CallRootEventFilter(exceptionCode, GetExceptionInformation()))
- {
- Assert(UNREACHED);
- }
- }
- __finally
- {
- // 0xE06D7363 is C++ exception code
- if (exceptionCode != 0 && exceptionCode != 0xE06D7363 && AbnormalTermination() && !IsDebuggerPresent())
- {
- scriptContext->GetThreadContext()->SetAbnormalExceptionCode(exceptionCode);
- scriptContext->GetThreadContext()->SetAbnormalExceptionRecord(&exceptionInfo);
- }
- }
- #endif
- //ret should never be null here
- Assert(ret);
- return ret;
- }
- Var JavascriptFunction::CallRootFunctionInternal(RecyclableObject* obj, Arguments args, ScriptContext * scriptContext, bool inScript)
- {
- #if DBG
- if (IsInAssert != 0)
- {
- // Just don't execute anything if we are in an assert
- Js::Throw::FatalInternalError();
- }
- #endif
- if (inScript)
- {
- Assert(!(args.Info.Flags & CallFlags_New));
- return JavascriptFunction::CallFunction<true>(obj, obj->GetEntryPoint(), args);
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- Js::Var varThis;
- if (PHASE_FORCE1(Js::EvalCompilePhase) && args.Info.Count == 0)
- {
- varThis = JavascriptOperators::OP_GetThis(scriptContext->GetLibrary()->GetUndefined(), kmodGlobal, scriptContext);
- args.Info.Flags = (Js::CallFlags)(args.Info.Flags | CallFlags_Eval);
- args.Info.Count = 1;
- args.Values = &varThis;
- }
- #endif
- Var varResult = nullptr;
- ThreadContext *threadContext;
- threadContext = scriptContext->GetThreadContext();
- JavascriptExceptionObject* pExceptionObject = NULL;
- bool hasCaller = scriptContext->GetHostScriptContext() ? !!scriptContext->GetHostScriptContext()->HasCaller() : false;
- Assert(scriptContext == obj->GetScriptContext());
- BEGIN_JS_RUNTIME_CALLROOT_EX(scriptContext, hasCaller)
- {
- scriptContext->VerifyAlive(true);
- try
- {
- varResult =
- args.Info.Flags & CallFlags_New ?
- CallAsConstructor(obj, /* overridingNewTarget = */nullptr, args, scriptContext) :
- CallFunction<true>(obj, obj->GetEntryPoint(), args);
- // A recent compiler bug 150148 can incorrectly eliminate catch block, temporary workaround
- if (threadContext == NULL)
- {
- throw JavascriptException(nullptr);
- }
- }
- catch (const JavascriptException& err)
- {
- pExceptionObject = err.GetAndClear();
- }
- if (pExceptionObject)
- {
- JavascriptExceptionOperators::DoThrowCheckClone(pExceptionObject, scriptContext);
- }
- }
- END_JS_RUNTIME_CALL(scriptContext);
- Assert(varResult != nullptr);
- return varResult;
- }
- Var JavascriptFunction::CallRootFunction(Arguments args, ScriptContext * scriptContext, bool inScript)
- {
- return JavascriptFunction::CallRootFunction(this, args, scriptContext, inScript);
- }
- #if DBG
- /*static*/
- void JavascriptFunction::CheckValidDebugThunk(ScriptContext* scriptContext, RecyclableObject *function)
- {
- Assert(scriptContext != nullptr);
- Assert(function != nullptr);
- if (scriptContext->IsScriptContextInDebugMode()
- && !scriptContext->IsInterpreted() && !CONFIG_FLAG(ForceDiagnosticsMode) // Does not work nicely if we change the default settings.
- && function->GetEntryPoint() != scriptContext->CurrentThunk
- && function->GetEntryPoint() != scriptContext->CurrentCrossSiteThunk
- && JavascriptFunction::Is(function))
- {
- JavascriptFunction *jsFunction = JavascriptFunction::FromVar(function);
- if (!jsFunction->IsBoundFunction()
- && !jsFunction->GetFunctionInfo()->IsDeferred()
- && (jsFunction->GetFunctionInfo()->GetAttributes() & FunctionInfo::DoNotProfile) != FunctionInfo::DoNotProfile
- && jsFunction->GetFunctionInfo() != &JavascriptExternalFunction::EntryInfo::WrappedFunctionThunk)
- {
- Js::FunctionProxy *proxy = jsFunction->GetFunctionProxy();
- if (proxy)
- {
- AssertMsg(proxy->HasValidEntryPoint(), "Function does not have valid entrypoint");
- }
- }
- }
- }
- #endif
- Var JavascriptFunction::CallAsConstructor(Var v, Var overridingNewTarget, Arguments args, ScriptContext* scriptContext, const Js::AuxArray<uint32> *spreadIndices)
- {
- Assert(v);
- Assert(args.Info.Flags & CallFlags_New);
- Assert(scriptContext);
- // newCount is ushort.
- if (args.Info.Count >= USHORT_MAX)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgListTooLarge);
- }
- AnalysisAssert(args.Info.Count < USHORT_MAX);
- // Create the empty object if necessary:
- // - Built-in constructor functions will return a new object of a specific type, so a new empty object does not need to
- // be created
- // - If the newTarget is specified and the function is base kind then the this object will be already created. So we can
- // just use it instead of creating a new one.
- // - For user-defined constructor functions, an empty object is created with the function's prototype
- Var resultObject = nullptr;
- if (overridingNewTarget != nullptr && args.Info.Count > 0)
- {
- resultObject = args.Values[0];
- }
- else
- {
- resultObject = JavascriptOperators::NewScObjectNoCtor(v, scriptContext);
- }
- // JavascriptOperators::NewScObject should have thrown if 'v' is not a constructor
- RecyclableObject* functionObj = RecyclableObject::FromVar(v);
- Var* newValues = args.Values;
- CallFlags newFlags = args.Info.Flags;
- ushort newCount = args.Info.Count;
- bool thisAlreadySpecified = false;
- if (overridingNewTarget != nullptr)
- {
- if (ScriptFunction::Is(functionObj) && ScriptFunction::FromVar(functionObj)->GetFunctionInfo()->IsClassConstructor())
- {
- thisAlreadySpecified = true;
- args.Values[0] = overridingNewTarget;
- }
- else
- {
- newCount++;
- newFlags = (CallFlags)(newFlags | CallFlags_NewTarget | CallFlags_ExtraArg);
- const unsigned STACK_ARGS_ALLOCA_THRESHOLD = 8; // Number of stack args we allow before using _alloca
- Var stackArgs[STACK_ARGS_ALLOCA_THRESHOLD];
- if (newCount > STACK_ARGS_ALLOCA_THRESHOLD)
- {
- PROBE_STACK(scriptContext, newCount * sizeof(Var) + Js::Constants::MinStackDefault); // args + function call
- newValues = (Var*)_alloca(newCount * sizeof(Var));
- }
- else
- {
- newValues = stackArgs;
- }
- for (unsigned int i = 0; i < args.Info.Count; i++)
- {
- newValues[i] = args.Values[i];
- }
- #pragma prefast(suppress:6386, "The index is within the bounds")
- newValues[args.Info.Count] = overridingNewTarget;
- }
- }
- // Call the constructor function:
- // - If this is not already specified as the overriding new target in Reflect.construct a class case, then
- // - Pass in the new empty object as the 'this' parameter. This can be null if an empty object was not created.
- if (!thisAlreadySpecified)
- {
- newValues[0] = resultObject;
- }
- CallInfo newCallInfo(newFlags, newCount);
- Arguments newArgs(newCallInfo, newValues);
- if (JavascriptProxy::Is(v))
- {
- JavascriptProxy* proxy = JavascriptProxy::FromVar(v);
- return proxy->ConstructorTrap(newArgs, scriptContext, spreadIndices);
- }
- #if DBG
- if (scriptContext->IsScriptContextInDebugMode())
- {
- CheckValidDebugThunk(scriptContext, functionObj);
- }
- #endif
- Var functionResult;
- if (spreadIndices != nullptr)
- {
- functionResult = CallSpreadFunction(functionObj, newArgs, spreadIndices);
- }
- else
- {
- functionResult = CallFunction<true>(functionObj, functionObj->GetEntryPoint(), newArgs);
- }
- return
- FinishConstructor(
- functionResult,
- resultObject,
- JavascriptFunction::Is(functionObj) && functionObj->GetScriptContext() == scriptContext ?
- JavascriptFunction::FromVar(functionObj) :
- nullptr,
- overridingNewTarget != nullptr);
- }
- Var JavascriptFunction::FinishConstructor(
- const Var constructorReturnValue,
- Var newObject,
- JavascriptFunction *const function,
- bool hasOverridingNewTarget)
- {
- Assert(constructorReturnValue);
- // CONSIDER: Using constructorCache->ctorHasNoExplicitReturnValue to speed up this interpreter code path.
- if (JavascriptOperators::IsObject(constructorReturnValue))
- {
- newObject = constructorReturnValue;
- }
- // #3217: Cases with overriding newTarget are not what constructor cache is intended for;
- // Bypass constructor cache to avoid prototype mismatch/confusion.
- if (function && function->GetConstructorCache()->NeedsUpdateAfterCtor() && !hasOverridingNewTarget)
- {
- JavascriptOperators::UpdateNewScObjectCache(function, newObject, function->GetScriptContext());
- }
- return newObject;
- }
- Var JavascriptFunction::EntrySpreadCall(const Js::AuxArray<uint32> *spreadIndices, RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- RUNTIME_ARGUMENTS(args, spreadIndices, function, callInfo);
- return JavascriptFunction::CallSpreadFunction(function, args, spreadIndices);
- }
- uint32 JavascriptFunction::GetSpreadSize(const Arguments args, const Js::AuxArray<uint32> *spreadIndices, ScriptContext *scriptContext)
- {
- // Work out the expanded number of arguments.
- uint32 totalLength = args.Info.Count - spreadIndices->count;
- ::Math::RecordOverflowPolicy overflow;
- for (unsigned i = 0; i < spreadIndices->count; ++i)
- {
- uint32 elementLength = JavascriptArray::GetSpreadArgLen(args[spreadIndices->elements[i]], scriptContext);
- totalLength = UInt32Math::Add(totalLength, elementLength, overflow);
- }
- if (totalLength >= CallInfo::kMaxCountArgs || overflow.HasOverflowed())
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgListTooLarge);
- }
- return totalLength;
- }
- void JavascriptFunction::SpreadArgs(const Arguments args, Arguments& destArgs, const Js::AuxArray<uint32> *spreadIndices, ScriptContext *scriptContext)
- {
- Assert(args.Values != nullptr);
- Assert(destArgs.Values != nullptr);
- CallInfo callInfo = args.Info;
- size_t destArgsByteSize = destArgs.Info.Count * sizeof(Var);
- destArgs.Values[0] = args[0];
- // Iterate over the arguments, spreading inline. We skip 'this'.
- uint32 argsIndex = 1;
- for (unsigned i = 1, spreadArgIndex = 0; i < callInfo.Count; ++i)
- {
- uint32 spreadIndex = spreadIndices->elements[spreadArgIndex]; // Next index to be spread.
- if (i < spreadIndex)
- {
- // Copy everything until the next spread index.
- js_memcpy_s(destArgs.Values + argsIndex,
- destArgsByteSize - (argsIndex * sizeof(Var)),
- args.Values + i,
- (spreadIndex - i) * sizeof(Var));
- argsIndex += spreadIndex - i;
- i = spreadIndex - 1;
- continue;
- }
- else if (i > spreadIndex)
- {
- // Copy everything after the last spread index.
- js_memcpy_s(destArgs.Values + argsIndex,
- destArgsByteSize - (argsIndex * sizeof(Var)),
- args.Values + i,
- (args.Info.Count - i) * sizeof(Var));
- break;
- }
- else
- {
- // Expand the spread element.
- Var instance = args[spreadIndex];
- if (SpreadArgument::Is(instance))
- {
- SpreadArgument* spreadedArgs = SpreadArgument::FromVar(instance);
- uint size = spreadedArgs->GetArgumentSpreadCount();
- if (size > 0)
- {
- const Var * spreadBuffer = spreadedArgs->GetArgumentSpread();
- js_memcpy_s(destArgs.Values + argsIndex,
- size * sizeof(Var),
- spreadBuffer,
- size * sizeof(Var));
- argsIndex += size;
- }
- }
- else
- {
- Assert(JavascriptOperators::IsUndefinedObject(instance));
- destArgs.Values[argsIndex++] = instance;
- }
- if (spreadArgIndex < spreadIndices->count - 1)
- {
- spreadArgIndex++;
- }
- }
- }
- if (argsIndex > destArgs.Info.Count)
- {
- AssertMsg(false, "The array length has changed since we allocated the destArgs buffer?");
- Throw::FatalInternalError();
- }
- }
- Var JavascriptFunction::CallSpreadFunction(RecyclableObject* function, Arguments args, const Js::AuxArray<uint32> *spreadIndices)
- {
- ScriptContext* scriptContext = function->GetScriptContext();
- // Work out the expanded number of arguments.
- uint32 actualLength = GetSpreadSize(args, spreadIndices, scriptContext);
- // Allocate (if needed) space for the expanded arguments.
- Arguments outArgs(CallInfo(args.Info.Flags, 0), nullptr);
- outArgs.Info.Count = actualLength;
- Var stackArgs[STACK_ARGS_ALLOCA_THRESHOLD];
- size_t outArgsSize = 0;
- if (outArgs.Info.Count > STACK_ARGS_ALLOCA_THRESHOLD)
- {
- PROBE_STACK(scriptContext, outArgs.Info.Count * sizeof(Var) + Js::Constants::MinStackDefault); // args + function call
- outArgsSize = outArgs.Info.Count * sizeof(Var);
- outArgs.Values = (Var*)_alloca(outArgsSize);
- ZeroMemory(outArgs.Values, outArgsSize);
- }
- else
- {
- outArgs.Values = stackArgs;
- outArgsSize = STACK_ARGS_ALLOCA_THRESHOLD * sizeof(Var);
- ZeroMemory(outArgs.Values, outArgsSize); // We may not use all of the elements
- }
- SpreadArgs(args, outArgs, spreadIndices, scriptContext);
- return JavascriptFunction::CallFunction<true>(function, function->GetEntryPoint(), outArgs);
- }
- Var JavascriptFunction::CallFunction(Arguments args)
- {
- return JavascriptFunction::CallFunction<true>(this, this->GetEntryPoint(), args);
- }
- template Var JavascriptFunction::CallFunction<true>(RecyclableObject* function, JavascriptMethod entryPoint, Arguments args);
- template Var JavascriptFunction::CallFunction<false>(RecyclableObject* function, JavascriptMethod entryPoint, Arguments args);
- #if _M_IX86
- #ifdef ASMJS_PLAT
- template <> int JavascriptFunction::CallAsmJsFunction<int>(RecyclableObject * function, JavascriptMethod entryPoint, uint argc, Var * argv)
- {
- return CallAsmJsFunctionX86Thunk(function, entryPoint, argc, argv).retIntVal;
- }
- template <> int64 JavascriptFunction::CallAsmJsFunction<int64>(RecyclableObject * function, JavascriptMethod entryPoint, uint argc, Var * argv)
- {
- return CallAsmJsFunctionX86Thunk(function, entryPoint, argc, argv).retInt64Val;
- }
- template <> float JavascriptFunction::CallAsmJsFunction<float>(RecyclableObject * function, JavascriptMethod entryPoint, uint argc, Var * argv)
- {
- return CallAsmJsFunctionX86Thunk(function, entryPoint, argc, argv).retFloatVal;
- }
- template <> double JavascriptFunction::CallAsmJsFunction<double>(RecyclableObject * function, JavascriptMethod entryPoint, uint argc, Var * argv)
- {
- return CallAsmJsFunctionX86Thunk(function, entryPoint, argc, argv).retDoubleVal;
- }
- template <> AsmJsSIMDValue JavascriptFunction::CallAsmJsFunction<AsmJsSIMDValue>(RecyclableObject * function, JavascriptMethod entryPoint, uint argc, Var * argv)
- {
- return CallAsmJsFunctionX86Thunk(function, entryPoint, argc, argv).retSimdVal;
- }
- PossibleAsmJsReturnValues JavascriptFunction::CallAsmJsFunctionX86Thunk(RecyclableObject * function, JavascriptMethod entryPoint, uint argc, Var * argv)
- {
- enum {
- IsFloat = 1 << AsmJsRetType::Float,
- IsDouble = 1 << AsmJsRetType::Double,
- IsInt64 = 1 << AsmJsRetType::Int64,
- IsSimd =
- 1 << AsmJsRetType::Int32x4 |
- 1 << AsmJsRetType::Bool32x4 |
- 1 << AsmJsRetType::Bool16x8 |
- 1 << AsmJsRetType::Bool8x16 |
- 1 << AsmJsRetType::Float32x4 |
- 1 << AsmJsRetType::Float64x2 |
- 1 << AsmJsRetType::Int16x8 |
- 1 << AsmJsRetType::Int8x16 |
- 1 << AsmJsRetType::Uint32x4 |
- 1 << AsmJsRetType::Uint16x8 |
- 1 << AsmJsRetType::Uint8x16,
- CannotUseEax = IsFloat | IsDouble | IsInt64 | IsSimd
- };
- AsmJsFunctionInfo* asmInfo = ((ScriptFunction*)function)->GetFunctionBody()->GetAsmJsFunctionInfo();
- Assert((uint)((ArgSlot)asmInfo->GetArgCount() + 1) == (uint)(asmInfo->GetArgCount() + 1));
- uint argsSize = asmInfo->GetArgByteSize();
- uint alignedSize = ::Math::Align<int32>(argsSize, 8);
- ScriptContext * scriptContext = function->GetScriptContext();
- PROBE_STACK_CALL(scriptContext, function, alignedSize);
- PossibleAsmJsReturnValues retVals;
- AsmJsRetType::Which retType = asmInfo->GetReturnType().which();
- void *data = nullptr;
- void *savedEsp = nullptr;
- __asm
- {
- // Save ESP
- mov savedEsp, esp;
- mov eax, alignedSize;
- // Make sure we don't go beyond guard page
- cmp eax, 0x1000;
- jge alloca_probe;
- sub esp, eax;
- jmp dbl_align;
- alloca_probe :
- // Use alloca to allocate more then a page size
- // Alloca assumes eax, contains size, and adjust ESP while
- // probing each page.
- call _alloca_probe_16;
- dbl_align :
- and esp,-8
- mov data, esp;
- }
- {
- Var* outParam = argv + 1;
- void* dest = (void*)data;
- memmove(dest, outParam, argsSize);
- }
- // call variable argument function provided in entryPoint
- __asm
- {
- #ifdef _CONTROL_FLOW_GUARD
- // verify that the call target is valid
- mov ecx, entryPoint
- call[__guard_check_icall_fptr]
- ; no need to restore ecx('call entryPoint' is a __cdecl call)
- #endif
- push function;
- call entryPoint;
- push edx; // save possible int64 return value
- mov ecx, retType;
- mov edx, 1;
- shl edx, cl;
- pop ecx; // restore possible int64 return value
- and edx, CannotUseEax;
- jz FromEax;
- and edx, ~IsInt64;
- jz FromEaxEcx;
- and edx, ~IsFloat;
- jz FromXmmWord;
- and edx, ~IsDouble;
- jz FromXmmDWord;
- // simd
- movups retVals.retSimdVal, xmm0;
- jmp end
- FromEax:
- mov retVals.retIntVal, eax;
- jmp end;
- FromEaxEcx:
- mov retVals.retIntVal, eax;
- mov retVals.retIntVal + 4, ecx;
- jmp end;
- FromXmmWord:
- movss retVals.retFloatVal, xmm0;
- jmp end;
- FromXmmDWord:
- movsd retVals.retDoubleVal, xmm0;
- end:
- // Restore ESP
- mov esp, savedEsp;
- }
- return retVals;
- }
- #endif
- #ifdef __clang__
- void __cdecl _alloca_probe_16()
- {
- // todo: fix this!!!
- abort();
- __asm
- {
- push ecx
- lea ecx, [esp + 8]
- sub ecx, eax
- and ecx, (16 - 1)
- add eax, ecx
- ret
- }
- }
- #endif
- static Var LocalCallFunction(RecyclableObject* function,
- JavascriptMethod entryPoint, Arguments args, bool doStackProbe)
- {
- Js::Var varResult;
- #if DBG && ENABLE_NATIVE_CODEGEN
- CheckIsExecutable(function, entryPoint);
- #endif
- // compute size of stack to reserve
- CallInfo callInfo = args.Info;
- uint argsSize = callInfo.Count * sizeof(Var);
- ScriptContext * scriptContext = function->GetScriptContext();
- if (doStackProbe)
- {
- PROBE_STACK_CALL(scriptContext, function, argsSize);
- }
- void *data;
- void *savedEsp;
- __asm {
- // Save ESP
- mov savedEsp, esp
- mov eax, argsSize
- // Make sure we don't go beyond guard page
- cmp eax, 0x1000
- jge alloca_probe
- sub esp, eax
- jmp dbl_align
- alloca_probe:
- // Use alloca to allocate more then a page size
- // Alloca assumes eax, contains size, and adjust ESP while
- // probing each page.
- call _alloca_probe_16
- dbl_align:
- // 8-byte align frame to improve floating point perf of our JIT'd code.
- and esp, -8
- mov data, esp
- }
- {
- Var* dest = (Var*)data;
- Var* src = args.Values;
- for(unsigned int i =0; i < callInfo.Count; i++)
- {
- dest[i] = src[i];
- }
- }
- // call variable argument function provided in entryPoint
- __asm
- {
- #ifdef _CONTROL_FLOW_GUARD
- // verify that the call target is valid
- mov ecx, entryPoint
- call [__guard_check_icall_fptr]
- ; no need to restore ecx ('call entryPoint' is a __cdecl call)
- #endif
- push callInfo
- push function
- call entryPoint
- // Restore ESP
- mov esp, savedEsp
- // save the return value from realsum.
- mov varResult, eax;
- }
- return varResult;
- }
- // clang fails to create the labels,
- // when __asm op is under a template function
- template <bool doStackProbe>
- Var JavascriptFunction::CallFunction(RecyclableObject* function,
- JavascriptMethod entryPoint, Arguments args)
- {
- return LocalCallFunction(function, entryPoint, args, doStackProbe);
- }
- #elif _M_X64
- template <bool doStackProbe>
- Var JavascriptFunction::CallFunction(RecyclableObject *function, JavascriptMethod entryPoint, Arguments args)
- {
- // compute size of stack to reserve and make sure we have enough stack.
- CallInfo callInfo = args.Info;
- uint argsSize = callInfo.Count * sizeof(Var);
- if (doStackProbe == true)
- {
- PROBE_STACK_CALL(function->GetScriptContext(), function, argsSize);
- }
- #if DBG && ENABLE_NATIVE_CODEGEN
- CheckIsExecutable(function, entryPoint);
- #endif
- return amd64_CallFunction(function, entryPoint, args.Info, args.Info.Count, &args.Values[0]);
- }
- #elif defined(_M_ARM)
- extern "C"
- {
- extern Var arm_CallFunction(JavascriptFunction* function, CallInfo info, Var* values, JavascriptMethod entryPoint);
- }
- template <bool doStackProbe>
- Var JavascriptFunction::CallFunction(RecyclableObject* function, JavascriptMethod entryPoint, Arguments args)
- {
- // compute size of stack to reserve and make sure we have enough stack.
- CallInfo callInfo = args.Info;
- uint argsSize = callInfo.Count * sizeof(Var);
- if (doStackProbe)
- {
- PROBE_STACK_CALL(function->GetScriptContext(), function, argsSize);
- }
- #if DBG && ENABLE_NATIVE_CODEGEN
- CheckIsExecutable(function, entryPoint);
- #endif
- Js::Var varResult;
- //The ARM can pass 4 arguments via registers so handle the cases for 0 or 1 values without resorting to asm code
- //(so that the asm code can assume 0 or more values will go on the stack: putting -1 values on the stack is unhealthy).
- unsigned count = args.Info.Count;
- if (count == 0)
- {
- varResult = CALL_ENTRYPOINT(function->GetScriptContext()->GetThreadContext(),
- entryPoint, (JavascriptFunction*)function, args.Info);
- }
- else if (count == 1)
- {
- varResult = CALL_ENTRYPOINT(function->GetScriptContext()->GetThreadContext(),
- entryPoint, (JavascriptFunction*)function, args.Info, args.Values[0]);
- }
- else
- {
- varResult = arm_CallFunction((JavascriptFunction*)function, args.Info, args.Values, entryPoint);
- }
- return varResult;
- }
- #elif defined(_M_ARM64)
- extern "C"
- {
- extern Var arm64_CallFunction(JavascriptFunction* function, CallInfo info, Var* values, JavascriptMethod entryPoint);
- }
- template <bool doStackProbe>
- Var JavascriptFunction::CallFunction(RecyclableObject* function, JavascriptMethod entryPoint, Arguments args)
- {
- // compute size of stack to reserve and make sure we have enough stack.
- CallInfo callInfo = args.Info;
- uint argsSize = callInfo.Count * sizeof(Var);
- if (doStackProbe)
- {
- PROBE_STACK_CALL(function->GetScriptContext(), function, argsSize);
- }
- #if DBG && ENABLE_NATIVE_CODEGEN
- CheckIsExecutable(function, entryPoint);
- #endif
- Js::Var varResult;
- varResult = arm64_CallFunction((JavascriptFunction*)function, args.Info, args.Values, entryPoint);
- return varResult;
- }
- #else
- Var JavascriptFunction::CallFunction(RecyclableObject *function, JavascriptMethod entryPoint, Arguments args)
- {
- #if DBG && ENABLE_NATIVE_CODEGEN
- CheckIsExecutable(function, entryPoint);
- #endif
- #if 1
- Js::Throw::NotImplemented();
- return nullptr;
- #else
- Var varResult;
- switch (info.Count)
- {
- case 0:
- {
- varResult=entryPoint((JavascriptFunction*)function, args.Info);
- break;
- }
- case 1: {
- varResult=entryPoint(
- (JavascriptFunction*)function,
- args.Info,
- args.Values[0]);
- break;
- }
- case 2: {
- varResult=entryPoint(
- (JavascriptFunction*)function,
- args.Info,
- args.Values[0],
- args.Values[1]);
- break;
- }
- case 3: {
- varResult=entryPoint(
- (JavascriptFunction*)function,
- args.Info,
- args.Values[0],
- args.Values[1],
- args.Values[2]);
- break;
- }
- case 4: {
- varResult=entryPoint(
- (JavascriptFunction*)function,
- args.Info,
- args.Values[0],
- args.Values[1],
- args.Values[2],
- args.Values[3]);
- break;
- }
- case 5: {
- varResult=entryPoint(
- (JavascriptFunction*)function,
- args.Info,
- args.Values[0],
- args.Values[1],
- args.Values[2],
- args.Values[3],
- args.Values[4]);
- break;
- }
- case 6: {
- varResult=entryPoint(
- (JavascriptFunction*)function,
- args.Info,
- args.Values[0],
- args.Values[1],
- args.Values[2],
- args.Values[3],
- args.Values[4],
- args.Values[5]);
- break;
- }
- case 7: {
- varResult=entryPoint(
- (JavascriptFunction*)function,
- args.Info,
- args.Values[0],
- args.Values[1],
- args.Values[2],
- args.Values[3],
- args.Values[4],
- args.Values[5],
- args.Values[6]);
- break;
- }
- case 8: {
- varResult=entryPoint(
- (JavascriptFunction*)function,
- args.Info,
- args.Values[0],
- args.Values[1],
- args.Values[2],
- args.Values[3],
- args.Values[4],
- args.Values[5],
- args.Values[6],
- args.Values[7]);
- break;
- }
- case 9: {
- varResult=entryPoint(
- (JavascriptFunction*)function,
- args.Info,
- args.Values[0],
- args.Values[1],
- args.Values[2],
- args.Values[3],
- args.Values[4],
- args.Values[5],
- args.Values[6],
- args.Values[7],
- args.Values[8]);
- break;
- }
- default:
- ScriptContext* scriptContext = function->type->GetScriptContext();
- varResult = scriptContext->GetLibrary()->GetUndefined();
- AssertMsg(false, "CallFunction call with unsupported number of arguments");
- break;
- }
- #endif
- return varResult;
- }
- #endif
- Var JavascriptFunction::EntryToString(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, "Should always have implicit 'this'");
- if (args.Info.Count == 0 || !JavascriptFunction::Is(args[0]))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedFunction, _u("Function.prototype.toString"));
- }
- JavascriptFunction *pFunc = JavascriptFunction::FromVar(args[0]);
- // pFunc can be from a different script context if Function.prototype.toString is invoked via .call/.apply.
- // Marshal the resulting string to the current script context (that of the toString)
- return CrossSite::MarshalVar(scriptContext, pFunc->EnsureSourceString());
- }
- JavascriptString* JavascriptFunction::GetNativeFunctionDisplayString(ScriptContext *scriptContext, JavascriptString *name)
- {
- return GetNativeFunctionDisplayStringCommon<JavascriptString>(scriptContext, name);
- }
- JavascriptString* JavascriptFunction::GetLibraryCodeDisplayString(ScriptContext *scriptContext, PCWSTR displayName)
- {
- return GetLibraryCodeDisplayStringCommon<JavascriptString, JavascriptString*>(scriptContext, displayName);
- }
- #ifdef _M_IX86
- // This code is enabled by the -checkAlignment switch.
- // It verifies that all of our JS frames are 8 byte aligned.
- // Our alignments is based on aligning the return address of the function.
- // Note that this test can fail when Javascript functions are called directly
- // from helper functions. This could be fixed by making these calls through
- // CallFunction(), or by having the helper 8 byte align the frame itself before
- // the call. A lot of these though are not dealing with floats, so the cost
- // of doing the 8 byte alignment would outweigh the benefit...
- __declspec (naked)
- void JavascriptFunction::CheckAlignment()
- {
- _asm
- {
- test esp, 0x4
- je LABEL1
- ret
- LABEL1:
- call Throw::InternalError
- }
- }
- #else
- void JavascriptFunction::CheckAlignment()
- {
- // Note: in order to enable this on ARM, uncomment/fix code in LowerMD.cpp (LowerEntryInstr).
- }
- #endif
- BOOL JavascriptFunction::IsNativeAddress(ScriptContext * scriptContext, void * codeAddr)
- {
- #if ENABLE_NATIVE_CODEGEN
- return scriptContext->IsNativeAddress(codeAddr);
- #else
- return false;
- #endif
- }
- Js::JavascriptMethod JavascriptFunction::DeferredParse(ScriptFunction** functionRef)
- {
- BOOL fParsed;
- return Js::ScriptFunction::DeferredParseCore(functionRef, fParsed);
- }
- Js::JavascriptMethod JavascriptFunction::DeferredParseCore(ScriptFunction** functionRef, BOOL &fParsed)
- {
- // Do the actual deferred parsing and byte code generation, passing the new entry point to the caller.
- ParseableFunctionInfo* functionInfo = (*functionRef)->GetParseableFunctionInfo();
- FunctionBody* funcBody = nullptr;
- Assert(functionInfo);
- ScriptFunctionWithInlineCache * funcObjectWithInlineCache = ScriptFunctionWithInlineCache::Is(*functionRef) ? ScriptFunctionWithInlineCache::FromVar(*functionRef) : nullptr;
- if (functionInfo->IsDeferredParseFunction())
- {
- if (funcObjectWithInlineCache)
- {
- // If inline caches were populated from a function body that has been redeferred, the caches have been cleaned up,
- // so clear the pointers. REVIEW: Is this a perf loss in some cases?
- funcObjectWithInlineCache->ClearBorrowedInlineCacheOnFunctionObject();
- }
- funcBody = functionInfo->Parse(functionRef);
- fParsed = funcBody->IsFunctionParsed() ? TRUE : FALSE;
- #if ENABLE_PROFILE_INFO
- // This is the first call to the function, ensure dynamic profile info
- funcBody->EnsureDynamicProfileInfo();
- #endif
- }
- else
- {
- funcBody = functionInfo->GetFunctionBody();
- Assert(funcBody != nullptr);
- Assert(!funcBody->IsDeferredParseFunction());
- }
- DebugOnly(JavascriptMethod directEntryPoint = funcBody->GetDirectEntryPoint(funcBody->GetDefaultEntryPointInfo()));
- #if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
- Assert(directEntryPoint != DefaultDeferredParsingThunk
- && directEntryPoint != ProfileDeferredParsingThunk);
- #else // !ENABLE_SCRIPT_PROFILING && !ENABLE_SCRIPT_DEBUGGING
- Assert(directEntryPoint != DefaultDeferredParsingThunk);
- #endif
- JavascriptMethod thunkEntryPoint = (*functionRef)->UpdateUndeferredBody(funcBody);
- if (funcObjectWithInlineCache && !funcObjectWithInlineCache->GetHasOwnInlineCaches())
- {
- // If the function object needs to use the inline caches from the function body, point them to the
- // function body's caches. This is required in two redeferral cases:
- //
- // 1. We might have cleared the caches on the function object (ClearBorrowedInlineCacheOnFunctionObject)
- // above if the function body was redeferred.
- // 2. Another function object could have been called before and undeferred the function body, thereby creating
- // new inline caches. This function object would still be pointing to the old ones and needs updating.
- funcObjectWithInlineCache->SetInlineCachesFromFunctionBody();
- }
- return thunkEntryPoint;
- }
- void JavascriptFunction::ReparseAsmJsModule(ScriptFunction** functionRef)
- {
- ParseableFunctionInfo* functionInfo = (*functionRef)->GetParseableFunctionInfo();
- Assert(functionInfo);
- functionInfo->GetFunctionBody()->AddDeferParseAttribute();
- functionInfo->GetFunctionBody()->ResetEntryPoint();
- functionInfo->GetFunctionBody()->ResetInParams();
- FunctionBody * funcBody = functionInfo->Parse(functionRef);
- #if ENABLE_PROFILE_INFO
- // This is the first call to the function, ensure dynamic profile info
- funcBody->EnsureDynamicProfileInfo();
- #endif
- (*functionRef)->UpdateUndeferredBody(funcBody);
- }
- // Thunk for handling calls to functions that have not had byte code generated for them.
- #if _M_IX86
- __declspec(naked)
- Var JavascriptFunction::DeferredParsingThunk(RecyclableObject* function, CallInfo callInfo, ...)
- {
- __asm
- {
- push ebp
- mov ebp, esp
- lea eax, [esp+8] // load the address of the function os that if we need to box, we can patch it up
- push eax
- call JavascriptFunction::DeferredParse
- #ifdef _CONTROL_FLOW_GUARD
- // verify that the call target is valid
- mov ecx, eax
- call[__guard_check_icall_fptr]
- mov eax, ecx
- #endif
- pop ebp
- jmp eax
- }
- }
- #elif defined(_M_X64) || defined(_M_ARM32_OR_ARM64)
- //Do nothing: the implementation of JavascriptFunction::DeferredParsingThunk is declared (appropriately decorated) in
- // Library\amd64\javascriptfunctiona.asm
- // Library\arm\arm_DeferredParsingThunk.asm
- // Library\arm64\arm64_DeferredParsingThunk.asm
- #else
- Var JavascriptFunction::DeferredParsingThunk(RecyclableObject* function, CallInfo callInfo, ...)
- {
- Js::Throw::NotImplemented();
- return nullptr;
- }
- #endif
- ConstructorCache* JavascriptFunction::EnsureValidConstructorCache()
- {
- Assert(this->constructorCache != nullptr);
- this->constructorCache = ConstructorCache::EnsureValidInstance(this->constructorCache, this->GetScriptContext());
- return this->constructorCache;
- }
- void JavascriptFunction::ResetConstructorCacheToDefault()
- {
- Assert(this->constructorCache != nullptr);
- if (!this->constructorCache->IsDefault())
- {
- this->constructorCache = &ConstructorCache::DefaultInstance;
- }
- }
- // Thunk for handling calls to functions that have not had byte code generated for them.
- #if _M_IX86
- __declspec(naked)
- Var JavascriptFunction::DeferredDeserializeThunk(RecyclableObject* function, CallInfo callInfo, ...)
- {
- __asm
- {
- push ebp
- mov ebp, esp
- push [esp+8]
- call JavascriptFunction::DeferredDeserialize
- #ifdef _CONTROL_FLOW_GUARD
- // verify that the call target is valid
- mov ecx, eax
- call[__guard_check_icall_fptr]
- mov eax, ecx
- #endif
- pop ebp
- jmp eax
- }
- }
- #elif (defined(_M_X64) || defined(_M_ARM32_OR_ARM64)) && defined(_MSC_VER)
- //Do nothing: the implementation of JavascriptFunction::DeferredParsingThunk is declared (appropriately decorated) in
- // Library\amd64\javascriptfunctiona.asm
- // Library\arm\arm_DeferredParsingThunk.asm
- // Library\arm64\arm64_DeferredParsingThunk.asm
- #else
- // xplat implement in
- // Library/amd64/JavascriptFunctionA.S
- #endif
- Js::JavascriptMethod JavascriptFunction::DeferredDeserialize(ScriptFunction* function)
- {
- FunctionInfo* funcInfo = function->GetFunctionInfo();
- Assert(funcInfo);
- FunctionBody* funcBody = nullptr;
- // If we haven't already deserialized this function, do so now
- // FunctionProxies could have gotten deserialized during the interpreter when
- // we tried to record the callsite info for the function which meant that it was a
- // target of a call. Or we could have deserialized the function info in another JavascriptFunctionInstance
- // In any case, fix up the function info if it's already been deserialized so that
- // we don't hold on to the proxy for too long, and rethunk it so that it directly
- // calls the default entry point the next time around
- if (funcInfo->IsDeferredDeserializeFunction())
- {
- DeferDeserializeFunctionInfo* deferDeserializeFunction = funcInfo->GetDeferDeserializeFunctionInfo();
- // This is the first call to the function, ensure dynamic profile info
- // Deserialize is a no-op if the function has already been deserialized
- funcBody = deferDeserializeFunction->Deserialize();
- #if ENABLE_PROFILE_INFO
- funcBody->EnsureDynamicProfileInfo();
- #endif
- }
- else
- {
- funcBody = funcInfo->GetFunctionBody();
- Assert(funcBody != nullptr);
- Assert(!funcBody->IsDeferredDeserializeFunction());
- }
- return function->UpdateUndeferredBody(funcBody);
- }
- void JavascriptFunction::SetEntryPoint(JavascriptMethod method)
- {
- this->GetDynamicType()->SetEntryPoint(method);
- }
- Var JavascriptFunction::EnsureSourceString()
- {
- return this->GetLibrary()->GetFunctionDisplayString();
- }
- /*
- *****************************************************************************************************************
- Conditions checked by instruction decoder (In sequential order)
- ******************************************************************************************************************
- 1) Exception Code is AV i.e STATUS_ACCESS_VIOLATION
- 2) Check if Rip is Native address
- 3) Get the function object from RBP (a fixed offset from RBP) and check for the following
- a. Not Null
- b. Ensure that the function object is heap allocated
- c. Ensure that the entrypointInfo is heap allocated
- d. Ensure that the functionbody is heap allocated
- e. Is a function
- f. Is AsmJs Function object for asmjs
- 4) Check if Array BufferLength > 0x10000 (64K), power of 2 if length is less than 2^24 or multiple of 2^24 and multiple of 0x1000(4K) for asmjs
- 5) Check If the instruction is valid
- a. Is one of the move instructions , i.e. mov, movsx, movzx, movsxd, movss or movsd
- b. Get the array buffer register and its value for asmjs
- c. Get the dst register(in case of load)
- d. Calculate the number of bytes read in order to get the length of the instruction , ensure that the length should never be greater than 15 bytes
- 6) Check that the Array buffer value is same as the one we passed in EntryPointInfo in asmjs
- 7) Set the dst reg if the instr type is load
- 8) Add the bytes read to Rip and set it as new Rip
- 9) Return EXCEPTION_CONTINUE_EXECUTION
- */
- #if ENABLE_NATIVE_CODEGEN
- #if defined(_M_IX86) || defined(_M_X64)
- class ExceptionFilterHelper
- {
- Js::ScriptFunction* m_func = nullptr;
- bool m_checkedForFunc = false;
- PEXCEPTION_POINTERS exceptionInfo;
- public:
- ExceptionFilterHelper(PEXCEPTION_POINTERS exceptionInfo) : exceptionInfo(exceptionInfo) {}
- PEXCEPTION_POINTERS GetExceptionInfo() const
- {
- return exceptionInfo;
- }
- uintptr_t GetFaultingAddress() const
- {
- // For AVs, the second element of ExceptionInformation array is address of inaccessible data
- // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082.aspx
- Assert(this->exceptionInfo->ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION);
- Assert(this->exceptionInfo->ExceptionRecord->NumberParameters >= 2);
- return exceptionInfo->ExceptionRecord->ExceptionInformation[1];
- }
- Var GetIPAddress() const
- {
- #if _M_IX86
- return (Var)exceptionInfo->ContextRecord->Eip;
- #elif _M_X64
- return (Var)exceptionInfo->ContextRecord->Rip;
- #else
- #error Not yet Implemented
- #endif
- }
- Var* GetAddressOfFuncObj() const
- {
- #if _M_IX86
- return (Var*)(exceptionInfo->ContextRecord->Ebp + 2 * sizeof(Var));
- #elif _M_X64
- return (Var*)(exceptionInfo->ContextRecord->Rbp + 2 * sizeof(Var));
- #else
- #error Not yet Implemented
- #endif
- }
- Js::ScriptFunction* GetScriptFunction()
- {
- if (m_checkedForFunc)
- {
- return m_func;
- }
- m_checkedForFunc = true;
- ThreadContext* threadContext = ThreadContext::GetContextForCurrentThread();
- // AV should come from JITed code, since we don't eliminate bound checks in interpreter
- if (!threadContext->IsNativeAddress(GetIPAddress()))
- {
- return nullptr;
- }
- Var* addressOfFuncObj = GetAddressOfFuncObj();
- if (!addressOfFuncObj || *addressOfFuncObj == nullptr || !ScriptFunction::Is(*addressOfFuncObj))
- {
- return nullptr;
- }
- Js::ScriptFunction* func = (Js::ScriptFunction*)(*addressOfFuncObj);
- RecyclerHeapObjectInfo heapObject;
- Recycler* recycler = threadContext->GetRecycler();
- bool isFuncObjHeapAllocated = recycler->FindHeapObject(func, FindHeapObjectFlags_NoFlags, heapObject); // recheck if this needs to be removed
- bool isEntryPointHeapAllocated = recycler->FindHeapObject(func->GetEntryPointInfo(), FindHeapObjectFlags_NoFlags, heapObject);
- bool isFunctionBodyHeapAllocated = recycler->FindHeapObject(func->GetFunctionBody(), FindHeapObjectFlags_NoFlags, heapObject);
- // ensure that all our objects are heap allocated
- if (!(isFuncObjHeapAllocated && isEntryPointHeapAllocated && isFunctionBodyHeapAllocated))
- {
- return nullptr;
- }
- m_func = func;
- return m_func;
- }
- };
- void CheckWasmMathException(int exceptionCode, ExceptionFilterHelper& helper)
- {
- if (CONFIG_FLAG(WasmMathExFilter) && (exceptionCode == STATUS_INTEGER_DIVIDE_BY_ZERO || exceptionCode == STATUS_INTEGER_OVERFLOW))
- {
- Js::ScriptFunction* func = helper.GetScriptFunction();
- if (func)
- {
- Js::FunctionBody* funcBody = func->GetFunctionBody();
- if (funcBody && funcBody->IsWasmFunction())
- {
- int32 code = exceptionCode == STATUS_INTEGER_DIVIDE_BY_ZERO ? WASMERR_DivideByZero : VBSERR_Overflow;
- JavascriptError::ThrowWebAssemblyRuntimeError(func->GetScriptContext(), code);
- }
- }
- }
- }
- // x64 specific exception filters
- #ifdef _M_X64
- ArrayAccessDecoder::InstructionData ArrayAccessDecoder::CheckValidInstr(BYTE* &pc, PEXCEPTION_POINTERS exceptionInfo) // get the reg operand and isLoad and
- {
- InstructionData instrData;
- uint prefixValue = 0;
- ArrayAccessDecoder::RexByteValue rexByteValue;
- bool isFloat = false;
- uint immBytes = 0;
- uint dispBytes = 0;
- bool isImmediate = false;
- bool isSIB = false;
- // Read first byte - check for prefix
- BYTE* beginPc = pc;
- if (((*pc) == 0x0F2) || ((*pc) == 0x0F3))
- {
- //MOVSD or MOVSS
- prefixValue = *pc;
- isFloat = true;
- pc++;
- }
- else if (*pc == 0x66)
- {
- prefixValue = *pc;
- pc++;
- }
- // Check for Rex Byte - After prefix we should have a rexByte if there is one
- if (*pc >= 0x40 && *pc <= 0x4F)
- {
- rexByteValue.rexValue = *pc;
- uint rexByte = *pc - 0x40;
- if (rexByte & 0x8)
- {
- rexByteValue.isW = true;
- }
- if (rexByte & 0x4)
- {
- rexByteValue.isR = true;
- }
- if (rexByte & 0x2)
- {
- rexByteValue.isX = true;
- }
- if (rexByte & 0x1)
- {
- rexByteValue.isB = true;
- }
- pc++;
- }
- // read opcode
- // Is one of the move instructions , i.e. mov, movsx, movzx, movsxd, movss or movsd
- switch (*pc)
- {
- //MOV - Store
- case 0x89:
- case 0x88:
- {
- pc++;
- instrData.isLoad = false;
- break;
- }
- //MOVSXD
- case 0x63:
- //MOV - Load
- case 0x8A:
- case 0x8B:
- {
- pc++;
- instrData.isLoad = true;
- break;
- }
- case 0x0F:
- {
- // more than one byte opcode and hence we will read pc multiple times
- pc++;
- //MOVSX
- if (*pc == 0xBE || *pc == 0xBF)
- {
- instrData.isLoad = true;
- }
- //MOVZX
- else if (*pc == 0xB6 || *pc == 0xB7)
- {
- instrData.isLoad = true;
- }
- //MOVSS - Load
- else if (*pc == 0x10 && prefixValue == 0xF3)
- {
- Assert(isFloat);
- instrData.isLoad = true;
- instrData.isFloat32 = true;
- }
- //MOVSS - Store
- else if (*pc == 0x11 && prefixValue == 0xF3)
- {
- Assert(isFloat);
- instrData.isLoad = false;
- instrData.isFloat32 = true;
- }
- //MOVSD - Load
- else if (*pc == 0x10 && prefixValue == 0xF2)
- {
- Assert(isFloat);
- instrData.isLoad = true;
- instrData.isFloat64 = true;
- }
- //MOVSD - Store
- else if (*pc == 0x11 && prefixValue == 0xF2)
- {
- Assert(isFloat);
- instrData.isLoad = false;
- instrData.isFloat64 = true;
- }
- //MOVUPS - Load
- else if (*pc == 0x10 && prefixValue == 0)
- {
- instrData.isLoad = true;
- instrData.isSimd = true;
- }
- //MOVUPS - Store
- else if (*pc == 0x11 && prefixValue == 0)
- {
- instrData.isLoad = false;
- instrData.isSimd = true;
- }
- else
- {
- instrData.isInvalidInstr = true;
- }
- pc++;
- break;
- }
- // Support Mov Immediates
- // MOV
- case 0xC6:
- case 0xC7:
- {
- instrData.isLoad = false;
- instrData.isFloat64 = false;
- isImmediate = true;
- if (*pc == 0xC6)
- {
- immBytes = 1;
- }
- else if (rexByteValue.isW) // For MOV, REX.W set means we have a 32 bit immediate value, which gets extended to 64 bit.
- {
- immBytes = 4;
- }
- else
- {
- if (prefixValue == 0x66)
- {
- immBytes = 2;
- }
- else
- {
- immBytes = 4;
- }
- }
- pc++;
- break;
- }
- default:
- instrData.isInvalidInstr = true;
- break;
- }
- // if the opcode is not a move return
- if (instrData.isInvalidInstr)
- {
- return instrData;
- }
- //Read ModR/M
- // Read the Src Reg and also check for SIB
- // Add the isR bit to SrcReg and get the actual SRCReg
- // Get the number of bytes for displacement
- //get mod bits
- BYTE modVal = *pc & 0xC0; // first two bits(7th and 6th bits)
- modVal >>= 6;
- //get the R/M bits
- BYTE rmVal = (*pc) & 0x07; // last 3 bits ( 0,1 and 2nd bits)
- //get the reg value
- BYTE dstReg = (*pc) & 0x38; // mask reg bits (3rd 4th and 5th bits)
- dstReg >>= 3;
- Assert(dstReg <= 0x07);
- Assert(modVal <= 0x03);
- Assert(rmVal <= 0x07);
- switch (modVal)
- {
- case 0x00:
- dispBytes = 0;
- break;
- case 0x01:
- dispBytes = 1;
- break;
- case 0x02:
- dispBytes = 4;
- break;
- default:
- instrData.isInvalidInstr = true;
- break;
- }
- if (instrData.isInvalidInstr)
- {
- return instrData;
- }
- // Get the R/M value and see if SIB is present , else get the buffer reg
- if (rmVal == 0x04)
- {
- isSIB = true;
- }
- else
- {
- instrData.bufferReg = rmVal;
- }
- // Get the RegByes from ModRM
- instrData.dstReg = dstReg;
- // increment the modrm byte
- pc++;
- // Check if we have SIB and in that case bufferReg should not be set
- if (isSIB)
- {
- Assert(!instrData.bufferReg);
- // Get the Base and Index Reg from SIB and ensure that Scale is zero
- // We don't care about the Index reg
- // Add the isB value from Rex and get the actual Base Reg
- // Get the base register
- // 6f. Get the array buffer register and its value
- instrData.bufferReg = (*pc % 8);
- pc++;
- }
- // check for the Rex.B value and append it to the base register
- if (rexByteValue.isB)
- {
- instrData.bufferReg |= 1 << 3;
- }
- // check for the Rex.R value and append it to the dst register
- if (rexByteValue.isR)
- {
- instrData.dstReg |= 1 << 3;
- }
- // Get the buffer address - this is always 64 bit GPR
- switch (instrData.bufferReg)
- {
- case 0x0:
- instrData.bufferValue = exceptionInfo->ContextRecord->Rax;
- break;
- case 0x1:
- instrData.bufferValue = exceptionInfo->ContextRecord->Rcx;
- break;
- case 0x2:
- instrData.bufferValue = exceptionInfo->ContextRecord->Rdx;
- break;
- case 0x3:
- instrData.bufferValue = exceptionInfo->ContextRecord->Rbx;
- break;
- case 0x4:
- instrData.bufferValue = exceptionInfo->ContextRecord->Rsp;
- break;
- case 0x5:
- // RBP wouldn't point to an array buffer
- instrData.bufferValue = NULL;
- break;
- case 0x6:
- instrData.bufferValue = exceptionInfo->ContextRecord->Rsi;
- break;
- case 0x7:
- instrData.bufferValue = exceptionInfo->ContextRecord->Rdi;
- break;
- case 0x8:
- instrData.bufferValue = exceptionInfo->ContextRecord->R8;
- break;
- case 0x9:
- instrData.bufferValue = exceptionInfo->ContextRecord->R9;
- break;
- case 0xA:
- instrData.bufferValue = exceptionInfo->ContextRecord->R10;
- break;
- case 0xB:
- instrData.bufferValue = exceptionInfo->ContextRecord->R11;
- break;
- case 0xC:
- instrData.bufferValue = exceptionInfo->ContextRecord->R12;
- break;
- case 0xD:
- instrData.bufferValue = exceptionInfo->ContextRecord->R13;
- break;
- case 0xE:
- instrData.bufferValue = exceptionInfo->ContextRecord->R14;
- break;
- case 0xF:
- instrData.bufferValue = exceptionInfo->ContextRecord->R15;
- break;
- default:
- instrData.isInvalidInstr = true;
- Assert(false);// should never reach here as validation is done before itself
- return instrData;
- }
- // add the pc for displacement , we don't need the displacement Byte value
- if (dispBytes > 0)
- {
- pc = pc + dispBytes;
- }
- instrData.instrSizeInByte = (uint)(pc - beginPc);
- if (isImmediate)
- {
- Assert(immBytes > 0);
- instrData.instrSizeInByte += immBytes;
- }
- // Calculate the number of bytes read in order to get the length of the instruction , ensure that the length should never be greater than 15 bytes
- if (instrData.instrSizeInByte > 15)
- {
- // no instr size can be greater than 15
- instrData.isInvalidInstr = true;
- }
- return instrData;
- }
- #if ENABLE_FAST_ARRAYBUFFER
- bool ResumeForOutOfBoundsArrayRefs(int exceptionCode, ExceptionFilterHelper& helper)
- {
- if (exceptionCode != STATUS_ACCESS_VIOLATION)
- {
- return false;
- }
- Js::ScriptFunction* func = helper.GetScriptFunction();
- if (!func)
- {
- return false;
- }
- Js::FunctionBody* funcBody = func->GetFunctionBody();
- bool isWAsmJs = funcBody->GetIsAsmJsFunction();
- bool isWasmOnly = funcBody->IsWasmFunction();
- if (isWAsmJs)
- {
- // some extra checks for asm.js because we have slightly more information that we can validate
- uintptr_t moduleMemory = (uintptr_t)((AsmJsScriptFunction*)func)->GetModuleMemory();
- if (!moduleMemory)
- {
- return false;
- }
- ArrayBuffer* arrayBuffer = nullptr;
- size_t reservationSize = 0;
- #ifdef ENABLE_WASM
- if (isWasmOnly)
- {
- WebAssemblyMemory* mem = *(WebAssemblyMemory**)(moduleMemory + WebAssemblyModule::GetMemoryOffset());
- arrayBuffer = mem->GetBuffer();
- reservationSize = MAX_WASM__ARRAYBUFFER_LENGTH;
- }
- else
- #endif
- {
- arrayBuffer = *(ArrayBuffer**)(moduleMemory + AsmJsModuleMemory::MemoryTableBeginOffset);
- reservationSize = MAX_ASMJS_ARRAYBUFFER_LENGTH;
- }
- if (!arrayBuffer || !arrayBuffer->GetBuffer())
- {
- // don't have a heap buffer for asm.js... so this shouldn't be an asm.js heap access
- return false;
- }
- uintptr_t bufferAddr = (uintptr_t)arrayBuffer->GetBuffer();
- uint bufferLength = arrayBuffer->GetByteLength();
- if (!isWasmOnly && !arrayBuffer->IsValidAsmJsBufferLength(bufferLength))
- {
- return false;
- }
- uintptr_t faultingAddr = helper.GetFaultingAddress();
- if (faultingAddr < bufferAddr)
- {
- return false;
- }
- if (faultingAddr >= bufferAddr + reservationSize)
- {
- return false;
- }
- }
- PEXCEPTION_POINTERS exceptionInfo = helper.GetExceptionInfo();
- BYTE* pc = (BYTE*)exceptionInfo->ExceptionRecord->ExceptionAddress;
- ArrayAccessDecoder::InstructionData instrData = ArrayAccessDecoder::CheckValidInstr(pc, exceptionInfo);
- // Check If the instruction is valid
- if (instrData.isInvalidInstr)
- {
- return false;
- }
- // If we didn't find the array buffer, ignore
- if (!instrData.bufferValue)
- {
- return false;
- }
- if (isWasmOnly)
- {
- JavascriptError::ThrowWebAssemblyRuntimeError(func->GetScriptContext(), WASMERR_ArrayIndexOutOfRange);
- }
- // SIMD loads/stores do bounds checks.
- if (instrData.isSimd)
- {
- return false;
- }
- // Set the dst reg if the instr type is load
- if (instrData.isLoad)
- {
- Var exceptionInfoReg = exceptionInfo->ContextRecord;
- Var* exceptionInfoIntReg = (Var*)((uint64)exceptionInfoReg + offsetof(CONTEXT, Rax)); // offset in the contextRecord for RAX , the assert below checks for any change in the exceptionInfo struct
- Var* exceptionInfoFloatReg = (Var*)((uint64)exceptionInfoReg + offsetof(CONTEXT, Xmm0));// offset in the contextRecord for XMM0 , the assert below checks for any change in the exceptionInfo struct
- Assert((DWORD64)*exceptionInfoIntReg == exceptionInfo->ContextRecord->Rax);
- Assert((uint64)*exceptionInfoFloatReg == exceptionInfo->ContextRecord->Xmm0.Low);
- if (instrData.isLoad)
- {
- double nanVal = JavascriptNumber::NaN;
- if (instrData.isFloat64)
- {
- double* destRegLocation = (double*)((uint64)exceptionInfoFloatReg + 16 * (instrData.dstReg));
- *destRegLocation = nanVal;
- }
- else if (instrData.isFloat32)
- {
- float* destRegLocation = (float*)((uint64)exceptionInfoFloatReg + 16 * (instrData.dstReg));
- *destRegLocation = (float)nanVal;
- }
- else
- {
- uint64* destRegLocation = (uint64*)((uint64)exceptionInfoIntReg + 8 * (instrData.dstReg));
- *destRegLocation = 0;
- }
- }
- }
- // Add the bytes read to Rip and set it as new Rip
- exceptionInfo->ContextRecord->Rip = exceptionInfo->ContextRecord->Rip + instrData.instrSizeInByte;
- return true;
- }
- #endif
- #endif
- #endif
- #endif
- int JavascriptFunction::CallRootEventFilter(int exceptionCode, PEXCEPTION_POINTERS exceptionInfo)
- {
- #if ENABLE_NATIVE_CODEGEN
- #if defined(_M_IX86) || defined(_M_X64)
- ExceptionFilterHelper helper(exceptionInfo);
- CheckWasmMathException(exceptionCode, helper);
- #if ENABLE_FAST_ARRAYBUFFER
- if (ResumeForOutOfBoundsArrayRefs(exceptionCode, helper))
- {
- return EXCEPTION_CONTINUE_EXECUTION;
- }
- #endif
- #endif
- #endif
- return EXCEPTION_CONTINUE_SEARCH;
- }
- #if DBG
- void JavascriptFunction::VerifyEntryPoint()
- {
- JavascriptMethod callEntryPoint = this->GetType()->GetEntryPoint();
- if (this->IsCrossSiteObject())
- {
- Assert(CrossSite::IsThunk(callEntryPoint));
- }
- else if (ScriptFunction::Is(this))
- {
- }
- else
- {
- JavascriptMethod originalEntryPoint = this->GetFunctionInfo()->GetOriginalEntryPoint();
- Assert(callEntryPoint == originalEntryPoint || callEntryPoint == ProfileEntryThunk
- || (this->GetScriptContext()->GetHostScriptContext()
- && this->GetScriptContext()->GetHostScriptContext()->IsHostCrossSiteThunk(callEntryPoint))
- );
- }
- }
- #endif
- /*static*/
- PropertyId const JavascriptFunction::specialPropertyIds[] =
- {
- PropertyIds::caller,
- PropertyIds::arguments
- };
- bool JavascriptFunction::HasRestrictedProperties() const
- {
- return !(
- this->functionInfo->IsClassMethod() ||
- this->functionInfo->IsClassConstructor() ||
- this->functionInfo->IsLambda() ||
- this->functionInfo->IsAsync() ||
- this->IsGeneratorFunction() ||
- this->IsBoundFunction() ||
- this->IsStrictMode()
- );
- }
- PropertyQueryFlags JavascriptFunction::HasPropertyQuery(PropertyId propertyId)
- {
- switch (propertyId)
- {
- case PropertyIds::caller:
- case PropertyIds::arguments:
- if (this->HasRestrictedProperties())
- {
- return PropertyQueryFlags::Property_Found;
- }
- break;
- case PropertyIds::length:
- if (this->IsScriptFunction())
- {
- return PropertyQueryFlags::Property_Found;
- }
- break;
- }
- return DynamicObject::HasPropertyQuery(propertyId);
- }
- BOOL JavascriptFunction::GetAccessors(PropertyId propertyId, Var *getter, Var *setter, ScriptContext * requestContext)
- {
- Assert(!this->IsBoundFunction());
- Assert(propertyId != Constants::NoProperty);
- Assert(getter);
- Assert(setter);
- Assert(requestContext);
- if (this->HasRestrictedProperties())
- {
- switch (propertyId)
- {
- case PropertyIds::caller:
- case PropertyIds::arguments:
- if (this->GetEntryPoint() == JavascriptFunction::PrototypeEntryPoint)
- {
- *setter = *getter = requestContext->GetLibrary()->GetThrowTypeErrorRestrictedPropertyAccessorFunction();
- return true;
- }
- break;
- }
- }
- return __super::GetAccessors(propertyId, getter, setter, requestContext);
- }
- DescriptorFlags JavascriptFunction::GetSetter(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- DescriptorFlags flags;
- if (GetSetterBuiltIns(propertyId, setterValue, info, requestContext, &flags))
- {
- return flags;
- }
- return __super::GetSetter(propertyId, setterValue, info, requestContext);
- }
- DescriptorFlags JavascriptFunction::GetSetter(JavascriptString* propertyNameString, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- DescriptorFlags flags;
- PropertyRecord const* propertyRecord;
- this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
- if (propertyRecord != nullptr && GetSetterBuiltIns(propertyRecord->GetPropertyId(), setterValue, info, requestContext, &flags))
- {
- return flags;
- }
- return __super::GetSetter(propertyNameString, setterValue, info, requestContext);
- }
- bool JavascriptFunction::GetSetterBuiltIns(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext, DescriptorFlags* descriptorFlags)
- {
- Assert(propertyId != Constants::NoProperty);
- Assert(setterValue);
- Assert(requestContext);
- switch (propertyId)
- {
- case PropertyIds::caller:
- case PropertyIds::arguments:
- if (this->HasRestrictedProperties()) {
- PropertyValueInfo::SetNoCache(info, this);
- if (this->GetEntryPoint() == JavascriptFunction::PrototypeEntryPoint)
- {
- *setterValue = requestContext->GetLibrary()->GetThrowTypeErrorRestrictedPropertyAccessorFunction();
- *descriptorFlags = Accessor;
- }
- else
- {
- *descriptorFlags = Data;
- }
- return true;
- }
- break;
- }
- return false;
- }
- BOOL JavascriptFunction::IsConfigurable(PropertyId propertyId)
- {
- if (DynamicObject::GetPropertyIndex(propertyId) == Constants::NoSlot)
- {
- switch (propertyId)
- {
- case PropertyIds::caller:
- case PropertyIds::arguments:
- if (this->HasRestrictedProperties())
- {
- return false;
- }
- break;
- case PropertyIds::length:
- if (this->IsScriptFunction() || this->IsBoundFunction())
- {
- return true;
- }
- break;
- }
- }
- return DynamicObject::IsConfigurable(propertyId);
- }
- BOOL JavascriptFunction::IsEnumerable(PropertyId propertyId)
- {
- if (DynamicObject::GetPropertyIndex(propertyId) == Constants::NoSlot)
- {
- switch (propertyId)
- {
- case PropertyIds::caller:
- case PropertyIds::arguments:
- if (this->HasRestrictedProperties())
- {
- return false;
- }
- break;
- case PropertyIds::length:
- if (this->IsScriptFunction())
- {
- return false;
- }
- break;
- }
- }
- return DynamicObject::IsEnumerable(propertyId);
- }
- BOOL JavascriptFunction::IsWritable(PropertyId propertyId)
- {
- if (DynamicObject::GetPropertyIndex(propertyId) == Constants::NoSlot)
- {
- switch (propertyId)
- {
- case PropertyIds::caller:
- case PropertyIds::arguments:
- if (this->HasRestrictedProperties())
- {
- return false;
- }
- break;
- case PropertyIds::length:
- if (this->IsScriptFunction())
- {
- return false;
- }
- break;
- }
- }
- return DynamicObject::IsWritable(propertyId);
- }
- BOOL JavascriptFunction::GetSpecialPropertyName(uint32 index, JavascriptString ** propertyName, ScriptContext * requestContext)
- {
- uint length = GetSpecialPropertyCount();
- if (index < length)
- {
- Assert(DynamicObject::GetPropertyIndex(specialPropertyIds[index]) == Constants::NoSlot);
- *propertyName = requestContext->GetPropertyString(specialPropertyIds[index]);
- return true;
- }
- if (index == length)
- {
- if (this->IsScriptFunction() || this->IsBoundFunction())
- {
- if (DynamicObject::GetPropertyIndex(PropertyIds::length) == Constants::NoSlot)
- {
- //Only for user defined functions length is a special property.
- *propertyName = requestContext->GetPropertyString(PropertyIds::length);
- return true;
- }
- }
- }
- return false;
- }
- // Returns the number of special non-enumerable properties this type has.
- uint JavascriptFunction::GetSpecialPropertyCount() const
- {
- return this->HasRestrictedProperties() ? _countof(specialPropertyIds) : 0;
- }
- // Returns the list of special non-enumerable properties for the type.
- PropertyId const * JavascriptFunction::GetSpecialPropertyIds() const
- {
- return specialPropertyIds;
- }
- PropertyQueryFlags JavascriptFunction::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- return JavascriptFunction::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
- }
- JavascriptFunction* JavascriptFunction::FindCaller(BOOL* foundThis, JavascriptFunction* nullValue, ScriptContext* requestContext)
- {
- ScriptContext* scriptContext = this->GetScriptContext();
- JavascriptFunction* funcCaller = nullValue;
- JavascriptStackWalker walker(scriptContext);
- if (walker.WalkToTarget(this))
- {
- *foundThis = TRUE;
- while (walker.GetCaller(&funcCaller))
- {
- if (walker.IsCallerGlobalFunction())
- {
- // Caller is global/eval. If it's eval, keep looking.
- // Otherwise, return null.
- if (walker.IsEvalCaller())
- {
- continue;
- }
- funcCaller = nullValue;
- }
- break;
- }
- if (funcCaller == nullptr)
- {
- // We no longer return Null objects as JavascriptFunctions, so we don't have to worry about
- // cross-context null objects. We do want to clean up null pointers though, since some call
- // later in this function may depend on non-nullptr calls.
- funcCaller = nullValue;
- }
- if (ScriptFunction::Is(funcCaller))
- {
- // If this is the internal function of a generator function then return the original generator function
- funcCaller = ScriptFunction::FromVar(funcCaller)->GetRealFunctionObject();
- // This function is escaping, so make sure there isn't some nested parent that has a cached scope.
- if (ScriptFunction::Is(funcCaller))
- {
- FrameDisplay * pFrameDisplay = Js::ScriptFunction::FromVar(funcCaller)->GetEnvironment();
- uint length = (uint)pFrameDisplay->GetLength();
- for (uint i = 0; i < length; i++)
- {
- void * scope = pFrameDisplay->GetItem(i);
- if (!Js::ScopeSlots::Is(scope) && Js::ActivationObjectEx::Is(scope))
- {
- Js::ActivationObjectEx::FromVar(scope)->InvalidateCachedScope();
- }
- }
- }
- }
- }
- return StackScriptFunction::EnsureBoxed(BOX_PARAM(funcCaller, nullptr, _u("caller")));
- }
- BOOL JavascriptFunction::GetCallerProperty(Var originalInstance, Var* value, ScriptContext* requestContext)
- {
- ScriptContext* scriptContext = this->GetScriptContext();
- *value = nullptr;
- if (this->IsStrictMode())
- {
- return false;
- }
- if (this->GetEntryPoint() == JavascriptFunction::PrototypeEntryPoint)
- {
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptFunction* accessor = requestContext->GetLibrary()->GetThrowTypeErrorRestrictedPropertyAccessorFunction();
- *value = CALL_FUNCTION(scriptContext->GetThreadContext(), accessor, CallInfo(1), originalInstance);
- }
- return true;
- }
- JavascriptFunction* nullValue = (JavascriptFunction*)requestContext->GetLibrary()->GetNull();
- if (this->IsLibraryCode()) // Hide .caller for builtins
- {
- *value = nullValue;
- return true;
- }
- // Use a stack walker to find this function's frame. If we find it, find its caller.
- BOOL foundThis = FALSE;
- JavascriptFunction* funcCaller = FindCaller(&foundThis, nullValue, requestContext);
- // WOOB #1142373. We are trying to get the caller in window.onerror = function(){alert(arguments.callee.caller);} case
- // window.onerror is called outside of JavascriptFunction::CallFunction loop, so the caller information is not available
- // in the stack to be found by the stack walker.
- // As we had already walked the stack at throw time retrieve the caller information stored in the exception object
- // The down side is that we can only find the top level caller at thrown time, and won't be able to find caller.caller etc.
- // We'll try to fetch the caller only if we can find the function on the stack, but we can't find the caller if and we are in
- // window.onerror scenario.
- *value = funcCaller;
- if (foundThis && funcCaller == nullValue && scriptContext->GetThreadContext()->HasUnhandledException())
- {
- Js::JavascriptExceptionObject* unhandledExceptionObject = scriptContext->GetThreadContext()->GetUnhandledExceptionObject();
- if (unhandledExceptionObject)
- {
- JavascriptFunction* exceptionFunction = unhandledExceptionObject->GetFunction();
- // This is for getcaller in window.onError. The behavior is different in different browsers
- if (exceptionFunction
- && scriptContext == exceptionFunction->GetScriptContext()
- && exceptionFunction->IsScriptFunction()
- && !exceptionFunction->GetFunctionBody()->GetIsGlobalFunc())
- {
- *value = exceptionFunction;
- }
- }
- }
- else if (foundThis && scriptContext != funcCaller->GetScriptContext())
- {
- HRESULT hr = scriptContext->GetHostScriptContext()->CheckCrossDomainScriptContext(funcCaller->GetScriptContext());
- if (S_OK != hr)
- {
- *value = nullValue;
- }
- else
- {
- *value = CrossSite::MarshalVar(requestContext, funcCaller);
- }
- }
- if (Js::JavascriptFunction::Is(*value) && Js::JavascriptFunction::FromVar(*value)->IsStrictMode())
- {
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- // ES5.15.3.5.4 [[Get]] (P) -- access to the 'caller' property of strict mode function results in TypeError.
- // Note that for caller coming from remote context (see the check right above) we can't call IsStrictMode()
- // unless CheckCrossDomainScriptContext succeeds. If it fails we don't know whether caller is strict mode
- // function or not and throw if it's not, so just return Null.
- JavascriptError::ThrowTypeError(scriptContext, JSERR_AccessRestrictedProperty);
- }
- }
- return true;
- }
- BOOL JavascriptFunction::GetArgumentsProperty(Var originalInstance, Var* value, ScriptContext* requestContext)
- {
- ScriptContext* scriptContext = this->GetScriptContext();
- if (this->IsStrictMode())
- {
- return false;
- }
- if (this->GetEntryPoint() == JavascriptFunction::PrototypeEntryPoint)
- {
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptFunction* accessor = requestContext->GetLibrary()->GetThrowTypeErrorRestrictedPropertyAccessorFunction();
- *value = CALL_FUNCTION(scriptContext->GetThreadContext(), accessor, CallInfo(1), originalInstance);
- }
- return true;
- }
- if (!this->IsScriptFunction())
- {
- // builtin function do not have an argument object - return null.
- *value = scriptContext->GetLibrary()->GetNull();
- return true;
- }
- // Use a stack walker to find this function's frame. If we find it, compute its arguments.
- // Note that we are currently unable to guarantee that the binding between formal arguments
- // and foo.arguments[n] will be maintained after this object is returned.
- JavascriptStackWalker walker(scriptContext);
- if (walker.WalkToTarget(this))
- {
- if (walker.IsCallerGlobalFunction())
- {
- *value = requestContext->GetLibrary()->GetNull();
- }
- else
- {
- Var args = nullptr;
- //Create a copy of the arguments and return it.
- const CallInfo callInfo = walker.GetCallInfo();
- args = JavascriptOperators::LoadHeapArguments(
- this, callInfo.Count - 1,
- walker.GetJavascriptArgs(),
- scriptContext->GetLibrary()->GetNull(),
- scriptContext->GetLibrary()->GetNull(),
- scriptContext,
- /* formalsAreLetDecls */ false);
- *value = args;
- }
- }
- else
- {
- *value = scriptContext->GetLibrary()->GetNull();
- }
- return true;
- }
- PropertyQueryFlags JavascriptFunction::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- BOOL result = JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext)) ? TRUE : FALSE;
- if (result)
- {
- if (propertyId == PropertyIds::prototype)
- {
- PropertyValueInfo::DisableStoreFieldCache(info);
- }
- }
- else
- {
- GetPropertyBuiltIns(originalInstance, propertyId, value, requestContext, &result);
- }
- return JavascriptConversion::BooleanToPropertyQueryFlags(result);
- }
- PropertyQueryFlags JavascriptFunction::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- BOOL result;
- PropertyRecord const* propertyRecord;
- this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
- result = JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::GetPropertyQuery(originalInstance, propertyNameString, value, info, requestContext)) ? TRUE : FALSE;
- if (result)
- {
- if (propertyRecord != nullptr && propertyRecord->GetPropertyId() == PropertyIds::prototype)
- {
- PropertyValueInfo::DisableStoreFieldCache(info);
- }
- return JavascriptConversion::BooleanToPropertyQueryFlags(result);
- }
- if (propertyRecord != nullptr)
- {
- GetPropertyBuiltIns(originalInstance, propertyRecord->GetPropertyId(), value, requestContext, &result);
- }
- return JavascriptConversion::BooleanToPropertyQueryFlags(result);
- }
- bool JavascriptFunction::GetPropertyBuiltIns(Var originalInstance, PropertyId propertyId, Var* value, ScriptContext* requestContext, BOOL* result)
- {
- if (propertyId == PropertyIds::caller && this->HasRestrictedProperties())
- {
- *result = GetCallerProperty(originalInstance, value, requestContext);
- return true;
- }
- if (propertyId == PropertyIds::arguments && this->HasRestrictedProperties())
- {
- *result = GetArgumentsProperty(originalInstance, value, requestContext);
- return true;
- }
- if (propertyId == PropertyIds::length)
- {
- FunctionProxy *proxy = this->GetFunctionProxy();
- if (proxy)
- {
- *value = TaggedInt::ToVarUnchecked(proxy->EnsureDeserialized()->GetReportedInParamsCount() - 1);
- *result = true;
- return true;
- }
- }
- return false;
- }
- BOOL JavascriptFunction::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
- {
- bool isReadOnly = false;
- switch (propertyId)
- {
- case PropertyIds::caller:
- if (this->HasRestrictedProperties())
- {
- isReadOnly = true;
- }
- break;
- case PropertyIds::arguments:
- if (this->HasRestrictedProperties())
- {
- isReadOnly = true;
- }
- break;
- case PropertyIds::length:
- if (this->IsScriptFunction())
- {
- isReadOnly = true;
- }
- break;
- }
- if (isReadOnly)
- {
- JavascriptError::ThrowCantAssignIfStrictMode(flags, this->GetScriptContext());
- return false;
- }
- BOOL result = DynamicObject::SetProperty(propertyId, value, flags, info);
- if (propertyId == PropertyIds::prototype || propertyId == PropertyIds::_symbolHasInstance)
- {
- PropertyValueInfo::SetNoCache(info, this);
- InvalidateConstructorCacheOnPrototypeChange();
- this->GetScriptContext()->GetThreadContext()->InvalidateIsInstInlineCachesForFunction(this);
- }
- return result;
- }
- BOOL JavascriptFunction::SetPropertyWithAttributes(PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects)
- {
- BOOL result = __super::SetPropertyWithAttributes(propertyId, value, attributes, info, flags, possibleSideEffects);
- if (propertyId == PropertyIds::prototype || propertyId == PropertyIds::_symbolHasInstance)
- {
- PropertyValueInfo::SetNoCache(info, this);
- InvalidateConstructorCacheOnPrototypeChange();
- this->GetScriptContext()->GetThreadContext()->InvalidateIsInstInlineCachesForFunction(this);
- }
- return result;
- }
- BOOL JavascriptFunction::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
- {
- PropertyRecord const * propertyRecord;
- this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
- if (propertyRecord != nullptr)
- {
- return JavascriptFunction::SetProperty(propertyRecord->GetPropertyId(), value, flags, info);
- }
- else
- {
- return DynamicObject::SetProperty(propertyNameString, value, flags, info);
- }
- }
- BOOL JavascriptFunction::DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags)
- {
- switch (propertyId)
- {
- case PropertyIds::caller:
- case PropertyIds::arguments:
- if (this->HasRestrictedProperties())
- {
- JavascriptError::ThrowCantDeleteIfStrictMode(flags, this->GetScriptContext(), this->GetScriptContext()->GetPropertyName(propertyId)->GetBuffer());
- return false;
- }
- break;
- case PropertyIds::length:
- if (this->IsScriptFunction())
- {
- JavascriptError::ThrowCantDeleteIfStrictMode(flags, this->GetScriptContext(), this->GetScriptContext()->GetPropertyName(propertyId)->GetBuffer());
- return false;
- }
- break;
- }
- BOOL result = DynamicObject::DeleteProperty(propertyId, flags);
- if (result && (propertyId == PropertyIds::prototype || propertyId == PropertyIds::_symbolHasInstance))
- {
- InvalidateConstructorCacheOnPrototypeChange();
- this->GetScriptContext()->GetThreadContext()->InvalidateIsInstInlineCachesForFunction(this);
- }
- return result;
- }
- BOOL JavascriptFunction::DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags)
- {
- JsUtil::CharacterBuffer<WCHAR> propertyName(propertyNameString->GetString(), propertyNameString->GetLength());
- if (BuiltInPropertyRecords::caller.Equals(propertyName) || BuiltInPropertyRecords::arguments.Equals(propertyName))
- {
- if (this->HasRestrictedProperties())
- {
- JavascriptError::ThrowCantDeleteIfStrictMode(flags, this->GetScriptContext(), propertyNameString->GetString());
- return false;
- }
- }
- else if (BuiltInPropertyRecords::length.Equals(propertyName))
- {
- if (this->IsScriptFunction())
- {
- JavascriptError::ThrowCantDeleteIfStrictMode(flags, this->GetScriptContext(), propertyNameString->GetString());
- return false;
- }
- }
- BOOL result = DynamicObject::DeleteProperty(propertyNameString, flags);
- if (result && (BuiltInPropertyRecords::prototype.Equals(propertyName) || BuiltInPropertyRecords::_symbolHasInstance.Equals(propertyName)))
- {
- InvalidateConstructorCacheOnPrototypeChange();
- this->GetScriptContext()->GetThreadContext()->InvalidateIsInstInlineCachesForFunction(this);
- }
- return result;
- }
- void JavascriptFunction::InvalidateConstructorCacheOnPrototypeChange()
- {
- Assert(this->constructorCache != nullptr);
- #if DBG_DUMP
- if (PHASE_TRACE1(Js::ConstructorCachePhase))
- {
- // This is under DBG_DUMP so we can allow a check
- ParseableFunctionInfo* body = this->GetFunctionProxy() != nullptr ? this->GetFunctionProxy()->EnsureDeserialized() : nullptr;
- const char16* ctorName = body != nullptr ? body->GetDisplayName() : _u("<unknown>");
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("CtorCache: before invalidating cache (0x%p) for ctor %s (%s): "), PointerValue(this->constructorCache), ctorName,
- body ? body->GetDebugNumberSet(debugStringBuffer) : _u("(null)"));
- this->constructorCache->Dump();
- Output::Print(_u("\n"));
- Output::Flush();
- }
- #endif
- this->constructorCache->InvalidateOnPrototypeChange();
- #if DBG_DUMP
- if (PHASE_TRACE1(Js::ConstructorCachePhase))
- {
- // This is under DBG_DUMP so we can allow a check
- ParseableFunctionInfo* body = this->GetFunctionProxy() != nullptr ? this->GetFunctionProxy()->EnsureDeserialized() : nullptr;
- const char16* ctorName = body != nullptr ? body->GetDisplayName() : _u("<unknown>");
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("CtorCache: after invalidating cache (0x%p) for ctor %s (%s): "), PointerValue(this->constructorCache), ctorName,
- body ? body->GetDebugNumberSet(debugStringBuffer) : _u("(null)"));
- this->constructorCache->Dump();
- Output::Print(_u("\n"));
- Output::Flush();
- }
- #endif
- }
- BOOL JavascriptFunction::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
- {
- JavascriptString * pString = NULL;
- Var sourceString = this->GetSourceString();
- if (sourceString == nullptr)
- {
- FunctionProxy* proxy = this->GetFunctionProxy();
- if (proxy)
- {
- ParseableFunctionInfo * func = proxy->EnsureDeserialized();
- Utf8SourceInfo* sourceInfo = func->GetUtf8SourceInfo();
- if (sourceInfo->GetIsLibraryCode())
- {
- charcount_t displayNameLength = 0;
- pString = JavascriptFunction::GetLibraryCodeDisplayString(this->GetScriptContext(), func->GetShortDisplayName(&displayNameLength));
- }
- else
- {
- charcount_t count = min(DIAG_MAX_FUNCTION_STRING, func->LengthInChars());
- utf8::DecodeOptions options = sourceInfo->IsCesu8() ? utf8::doAllowThreeByteSurrogates : utf8::doDefault;
- LPCUTF8 source = func->GetSource(_u("JavascriptFunction::GetDiagValueString"));
- size_t cbLength = sourceInfo->GetCbLength(_u("JavascriptFunction::GetDiagValueString"));
- size_t cbIndex = utf8::CharacterIndexToByteIndex(source, cbLength, count, options);
- utf8::DecodeUnitsInto(stringBuilder->AllocBufferSpace(count), source, source + cbIndex, options);
- stringBuilder->IncreaseCount(count);
- return TRUE;
- }
- }
- else
- {
- pString = GetLibrary()->GetFunctionDisplayString();
- }
- }
- else
- {
- if (TaggedInt::Is(sourceString))
- {
- pString = GetNativeFunctionDisplayString(this->GetScriptContext(), this->GetScriptContext()->GetPropertyString(TaggedInt::ToInt32(sourceString)));
- }
- else
- {
- Assert(JavascriptString::Is(sourceString));
- pString = JavascriptString::FromVar(sourceString);
- }
- }
- Assert(pString);
- stringBuilder->Append(pString->GetString(), pString->GetLength());
- return TRUE;
- }
- BOOL JavascriptFunction::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
- {
- stringBuilder->AppendCppLiteral(_u("Object, (Function)"));
- return TRUE;
- }
- JavascriptString* JavascriptFunction::GetDisplayNameImpl() const
- {
- Assert(this->GetFunctionProxy() != nullptr); // The caller should guarantee a proxy exists
- ParseableFunctionInfo * func = this->GetFunctionProxy()->EnsureDeserialized();
- charcount_t length = 0;
- const char16* name = func->GetShortDisplayName(&length);
- return DisplayNameHelper(name, length);
- }
- JavascriptString* JavascriptFunction::DisplayNameHelper(const char16* name, charcount_t length) const
- {
- ScriptContext* scriptContext = this->GetScriptContext();
- Assert(this->GetFunctionProxy() != nullptr); // The caller should guarantee a proxy exists
- ParseableFunctionInfo * func = this->GetFunctionProxy()->EnsureDeserialized();
- if (func->GetDisplayName() == Js::Constants::FunctionCode)
- {
- return LiteralString::NewCopyBuffer(Js::Constants::Anonymous, Js::Constants::AnonymousLength, scriptContext);
- }
- else if (func->GetIsAccessor())
- {
- const char16* accessorName = func->GetDisplayName();
- if (accessorName[0] == _u('g'))
- {
- return LiteralString::Concat(LiteralString::NewCopySz(_u("get "), scriptContext), LiteralString::NewCopyBuffer(name, length, scriptContext));
- }
- AssertMsg(accessorName[0] == _u('s'), "should be a set");
- return LiteralString::Concat(LiteralString::NewCopySz(_u("set "), scriptContext), LiteralString::NewCopyBuffer(name, length, scriptContext));
- }
- return LiteralString::NewCopyBuffer(name, length, scriptContext);
- }
- bool JavascriptFunction::GetFunctionName(JavascriptString** name) const
- {
- Assert(name != nullptr);
- FunctionProxy* proxy = this->GetFunctionProxy();
- JavascriptFunction* thisFunction = const_cast<JavascriptFunction*>(this);
- if (proxy || thisFunction->IsBoundFunction() || JavascriptGeneratorFunction::Test(thisFunction) || JavascriptAsyncFunction::Test(thisFunction))
- {
- *name = GetDisplayNameImpl();
- return true;
- }
- Assert(!ScriptFunction::Is(thisFunction));
- return GetSourceStringName(name);
- }
- bool JavascriptFunction::GetSourceStringName(JavascriptString** name) const
- {
- Assert(name != nullptr);
- ScriptContext* scriptContext = this->GetScriptContext();
- Var sourceString = this->GetSourceString();
- if (sourceString)
- {
- if (TaggedInt::Is(sourceString))
- {
- int32 propertyIdOfSourceString = TaggedInt::ToInt32(sourceString);
- *name = scriptContext->GetPropertyString(propertyIdOfSourceString);
- return true;
- }
- Assert(JavascriptString::Is(sourceString));
- *name = JavascriptString::FromVar(sourceString);
- return true;
- }
- return false;
- }
- JavascriptString* JavascriptFunction::GetDisplayName() const
- {
- ScriptContext* scriptContext = this->GetScriptContext();
- FunctionProxy* proxy = this->GetFunctionProxy();
- JavascriptLibrary* library = scriptContext->GetLibrary();
- if (proxy)
- {
- ParseableFunctionInfo * func = proxy->EnsureDeserialized();
- return LiteralString::NewCopySz(func->GetDisplayName(), scriptContext);
- }
- JavascriptString* sourceStringName = nullptr;
- if (GetSourceStringName(&sourceStringName))
- {
- return sourceStringName;
- }
- return library->GetFunctionDisplayString();
- }
- Var JavascriptFunction::GetTypeOfString(ScriptContext * requestContext)
- {
- return requestContext->GetLibrary()->GetFunctionTypeDisplayString();
- }
- // Check if this function is native/script library code
- bool JavascriptFunction::IsLibraryCode() const
- {
- return !this->IsScriptFunction() || this->GetFunctionProxy()->GetUtf8SourceInfo()->GetIsLibraryCode();
- }
- // Implementation of Function.prototype[@@hasInstance](V) as specified in 19.2.3.6 of ES6 spec
- Var JavascriptFunction::EntrySymbolHasInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (!JavascriptConversion::IsCallable(args[0]) || args.Info.Count < 2)
- {
- return JavascriptBoolean::ToVar(FALSE, scriptContext);
- }
- RecyclableObject * constructor = RecyclableObject::FromVar(args[0]);
- Var instance = args[1];
- Assert(JavascriptProxy::Is(constructor) || JavascriptFunction::Is(constructor));
- return JavascriptBoolean::ToVar(constructor->HasInstance(instance, scriptContext, NULL), scriptContext);
- }
- BOOL JavascriptFunction::HasInstance(Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache)
- {
- Var funcPrototype;
- if (this->GetTypeHandler()->GetHasKnownSlot0())
- {
- Assert(this->GetDynamicType()->GetTypeHandler()->GetPropertyId(scriptContext, (PropertyIndex)0) == PropertyIds::prototype);
- funcPrototype = this->GetSlot(0);
- }
- else
- {
- funcPrototype = JavascriptOperators::GetProperty(this, PropertyIds::prototype, scriptContext, nullptr);
- }
- funcPrototype = CrossSite::MarshalVar(scriptContext, funcPrototype);
- return JavascriptFunction::HasInstance(funcPrototype, instance, scriptContext, inlineCache, this);
- }
- BOOL JavascriptFunction::HasInstance(Var funcPrototype, Var instance, ScriptContext * scriptContext, IsInstInlineCache* inlineCache, JavascriptFunction *function)
- {
- BOOL result = FALSE;
- JavascriptBoolean * javascriptResult;
- //
- // if "instance" is not a JavascriptObject, return false
- //
- if (!JavascriptOperators::IsObject(instance))
- {
- // Only update the cache for primitive cache if it is empty already for the JIT fast path
- if (inlineCache && inlineCache->function == nullptr
- && scriptContext == function->GetScriptContext())// only register when function has same scriptContext
- {
- inlineCache->Cache(RecyclableObject::Is(instance) ?
- RecyclableObject::FromVar(instance)->GetType() : nullptr,
- function, scriptContext->GetLibrary()->GetFalse(), scriptContext);
- }
- return result;
- }
- // If we have an instance of inline cache, let's try to use it to speed up the operation.
- // We would like to catch all cases when we already know (by having checked previously)
- // that an object on the left of instance of has been created by a function on the right,
- // as well as when we already know the object on the left has not been created by a function on the right.
- // In practice, we can do so only if the function matches the function in the cache, and the object's type matches the
- // type in the cache. Notably, this typically means that if some of the objects evolved after construction,
- // while others did not, we will miss the cache for one of the two (sets of objects).
- // An important subtlety here arises when a function is called from different script contexts.
- // Suppose we called function foo from script context A, and we pass it an object o created in the same script context.
- // When function foo checks if object o is an instance of itself (function foo) for the first time (from context A) we will
- // populate the cache with function foo and object o's type (which is permanently bound to the script context A,
- // in which object o was created). If we later invoked function foo from script context B and perform the same instance-of check,
- // the function will still match the function in the cache (because objects' identities do not change during cross-context marshalling).
- // However, object o's type (even if it is of the same "shape" as before) will be different, because the object types are permanently
- // bound and unique to the script context from which they were created. Hence, the cache may miss, even if the function matches.
- if (inlineCache != nullptr)
- {
- Assert(function != nullptr);
- if (inlineCache->TryGetResult(instance, function, &javascriptResult))
- {
- return javascriptResult == scriptContext->GetLibrary()->GetTrue();
- }
- }
- // If we are here, then me must have missed the cache. This may be because:
- // a) the cache has never been populated in the first place,
- // b) the cache has been populated, but for an object of a different type (even if the object was created by the same constructor function),
- // c) the cache has been populated, but for a different function,
- // d) the cache has been populated, even for the same object type and function, but has since been invalidated, because the function's
- // prototype property has been changed (see JavascriptFunction::SetProperty and ThreadContext::InvalidateIsInstInlineCachesForFunction).
- // We may even miss the cache if we ask again about the very same object the very same function the cache was populated with.
- // This subtlety arises when a function is called from two (or more) different script contexts.
- // Suppose we called function foo from script context A, and passed it an object o created in the same script context.
- // When function foo checks if object o is an instance of itself (function foo) for the first time (from context A) we will
- // populate the cache with function foo and object o's type (which is permanently bound to the script context A,
- // in which object o was created). If we later invoked function foo from script context B and perform the same instance of check,
- // the function will still match the function in the cache (because objects' identities do not change during cross-context marshalling).
- // However, object o's type (even if it is of the same "shape" as before, and even if o is the very same object) will be different,
- // because the object types are permanently bound and unique to the script context from which they were created.
- Var prototype = JavascriptOperators::GetPrototype(RecyclableObject::FromVar(instance));
- if (!JavascriptOperators::IsObject(funcPrototype))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidPrototype);
- }
- // Since we missed the cache, we must now walk the prototype chain of the object to check if the given function's prototype is somewhere in
- // that chain. If it is, we return true. Otherwise (i.e., we hit the end of the chain before finding the function's prototype) we return false.
- while (JavascriptOperators::GetTypeId(prototype) != TypeIds_Null)
- {
- if (prototype == funcPrototype)
- {
- result = TRUE;
- break;
- }
- prototype = JavascriptOperators::GetPrototype(RecyclableObject::FromVar(prototype));
- }
- // Now that we know the answer, let's cache it for next time if we have a cache.
- if (inlineCache != NULL)
- {
- Assert(function != NULL);
- JavascriptBoolean * boolResult = result ? scriptContext->GetLibrary()->GetTrue() :
- scriptContext->GetLibrary()->GetFalse();
- Type * instanceType = RecyclableObject::FromVar(instance)->GetType();
- if (!instanceType->HasSpecialPrototype()
- && scriptContext == function->GetScriptContext()) // only register when function has same scriptContext, otherwise when scriptContext close
- // and the isInst inline cache chain will be broken by clearing the arenaAllocator
- {
- inlineCache->Cache(instanceType, function, boolResult, scriptContext);
- }
- }
- return result;
- }
- }
|