| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591 |
- //-------------------------------------------------------------------------------------------------------
- // 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"
- namespace Js
- {
- BOOL JavascriptProxy::Is(_In_ RecyclableObject* obj)
- {
- return JavascriptOperators::GetTypeId(obj) == TypeIds_Proxy;
- }
- BOOL JavascriptProxy::Is(_In_ Var obj)
- {
- return JavascriptOperators::GetTypeId(obj) == TypeIds_Proxy;
- }
- bool JavascriptProxy::IsRevoked() const
- {
- return (target == nullptr);
- }
- RecyclableObject* JavascriptProxy::GetTarget()
- {
- if (target == nullptr)
- {
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u(""));
- }
- return target;
- }
- RecyclableObject* JavascriptProxy::GetHandler()
- {
- if (handler == nullptr)
- {
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u(""));
- }
- return handler;
- }
- Var JavascriptProxy::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(ES6, Proxy, scriptContext);
- if (!(args.Info.Flags & CallFlags_New))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Proxy"));
- }
- JavascriptProxy* proxy = JavascriptProxy::Create(scriptContext, args);
- return proxy;
- }
- JavascriptProxy* JavascriptProxy::Create(ScriptContext* scriptContext, Arguments args)
- {
- // SkipDefaultNewObject function flag should have prevented the default object from
- // being created, except when call true a host dispatch.
- Var newTarget = args.GetNewTarget();
- bool isCtorSuperCall = JavascriptOperators::IsConstructorSuperCall(args);
- RecyclableObject* target, *handler;
- if (args.Info.Count < 3)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedProxyArgument);
- }
- if (!JavascriptOperators::IsObjectType(JavascriptOperators::GetTypeId(args[1])))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidProxyArgument, _u("target"));
- }
- target = DynamicObject::FromVar(args[1]);
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(target);
- #endif
- if (JavascriptProxy::Is(target))
- {
- if (JavascriptProxy::FromVar(target)->target == nullptr)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidProxyArgument, _u("target"));
- }
- }
- if (!JavascriptOperators::IsObjectType(JavascriptOperators::GetTypeId(args[2])))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidProxyArgument, _u("handler"));
- }
- handler = DynamicObject::FromVar(args[2]);
- if (JavascriptProxy::Is(handler))
- {
- if (JavascriptProxy::FromVar(handler)->handler == nullptr)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidProxyArgument, _u("handler"));
- }
- }
- JavascriptProxy* newProxy = RecyclerNew(scriptContext->GetRecycler(), JavascriptProxy, scriptContext->GetLibrary()->GetProxyType(), scriptContext, target, handler);
- if (JavascriptConversion::IsCallable(target))
- {
- newProxy->ChangeType();
- newProxy->GetDynamicType()->SetEntryPoint(JavascriptProxy::FunctionCallTrap);
- }
- return isCtorSuperCall ?
- JavascriptProxy::FromVar(JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), newProxy, nullptr, scriptContext)) :
- newProxy;
- }
- Var JavascriptProxy::EntryRevocable(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Proxy.revocable"));
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- if (args.Info.Flags & CallFlags_New)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Proxy.revocable"));
- }
- JavascriptProxy* proxy = JavascriptProxy::Create(scriptContext, args);
- JavascriptLibrary* library = scriptContext->GetLibrary();
- DynamicType* type = library->CreateFunctionWithConfigurableLengthType(&EntryInfo::Revoke);
- RuntimeFunction* revoker = RecyclerNewEnumClass(scriptContext->GetRecycler(),
- JavascriptLibrary::EnumFunctionClass, RuntimeFunction,
- type, &EntryInfo::Revoke);
- revoker->SetPropertyWithAttributes(Js::PropertyIds::length, Js::TaggedInt::ToVarUnchecked(2), PropertyConfigurable, NULL);
- revoker->SetInternalProperty(Js::InternalPropertyIds::RevocableProxy, proxy, PropertyOperationFlags::PropertyOperation_Force, nullptr);
- DynamicObject* obj = scriptContext->GetLibrary()->CreateObject(true, 2);
- JavascriptOperators::SetProperty(obj, obj, PropertyIds::proxy, proxy, scriptContext);
- JavascriptOperators::SetProperty(obj, obj, PropertyIds::revoke, revoker, scriptContext);
- return obj;
- }
- Var JavascriptProxy::EntryRevoke(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Proxy.revoke"));
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- Var revokableProxy;
- if (!function->GetInternalProperty(function, Js::InternalPropertyIds::RevocableProxy, &revokableProxy, nullptr, scriptContext))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidProxyArgument, _u(""));
- }
- TypeId typeId = JavascriptOperators::GetTypeId(revokableProxy);
- if (typeId == TypeIds_Null)
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- if (typeId != TypeIds_Proxy)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidProxyArgument, _u(""));
- }
- function->SetInternalProperty(Js::InternalPropertyIds::RevocableProxy, scriptContext->GetLibrary()->GetNull(), PropertyOperationFlags::PropertyOperation_Force, nullptr);
- (JavascriptProxy::FromVar(revokableProxy))->RevokeObject();
- return scriptContext->GetLibrary()->GetUndefined();
- }
- JavascriptProxy::JavascriptProxy(DynamicType * type) :
- DynamicObject(type),
- handler(nullptr),
- target(nullptr)
- {
- type->SetHasSpecialPrototype(true);
- }
- JavascriptProxy::JavascriptProxy(DynamicType * type, ScriptContext * scriptContext, RecyclableObject* target, RecyclableObject* handler) :
- DynamicObject(type),
- handler(handler),
- target(target)
- {
- type->SetHasSpecialPrototype(true);
- }
- void JavascriptProxy::RevokeObject()
- {
- handler = nullptr;
- target = nullptr;
- }
- BOOL JavascriptProxy::GetPropertyDescriptorTrap(PropertyId propertyId, PropertyDescriptor* resultDescriptor, ScriptContext* requestContext)
- {
- PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
- // Reject implicit call
- ThreadContext* threadContext = requestContext->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return FALSE;
- }
- //1. Let handler be the value of the[[ProxyHandler]] internal slot of O.
- Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
- //2. If handler is null, then throw a TypeError exception.
- if (handlerObj == nullptr)
- {
- if (!threadContext->RecordImplicitException())
- return FALSE;
- JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("getOwnPropertyDescriptor"));
- }
- //3. Let target be the value of the[[ProxyTarget]] internal slot of O.
- Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
- Assert((static_cast<DynamicType*>(GetType()))->GetTypeHandler()->GetPropertyCount() == 0 ||
- (static_cast<DynamicType*>(GetType()))->GetTypeHandler()->GetPropertyId(GetScriptContext(), 0) == InternalPropertyIds::WeakMapKeyMap);
- JavascriptFunction* gOPDMethod = GetMethodHelper(PropertyIds::getOwnPropertyDescriptor, requestContext);
- //7. If trap is undefined, then
- // a.Return the result of calling the[[GetOwnProperty]] internal method of target with argument P.
- if (nullptr == gOPDMethod || GetScriptContext()->IsHeapEnumInProgress())
- {
- resultDescriptor->SetFromProxy(false);
- return JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, resultDescriptor);
- }
- Var propertyName = GetName(requestContext, propertyId);
- Assert(JavascriptString::Is(propertyName) || JavascriptSymbol::Is(propertyName));
- //8. Let trapResultObj be the result of calling the[[Call]] internal method of trap with handler as the this value and a new List containing target and P.
- //9. ReturnIfAbrupt(trapResultObj).
- //10. If Type(trapResultObj) is neither Object nor Undefined, then throw a TypeError exception.
- Var getResult = threadContext->ExecuteImplicitCall(gOPDMethod, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, gOPDMethod, CallInfo(CallFlags_Value, 3), handlerObj, targetObj, propertyName);
- });
- TypeId getResultTypeId = JavascriptOperators::GetTypeId(getResult);
- if (StaticType::Is(getResultTypeId) && getResultTypeId != TypeIds_Undefined)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_NeedObject, _u("getOwnPropertyDescriptor"));
- }
- //11. Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
- //12. ReturnIfAbrupt(targetDesc).
- PropertyDescriptor targetDescriptor;
- BOOL hasProperty = JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetDescriptor);
- //13. If trapResultObj is undefined, then
- //a.If targetDesc is undefined, then return undefined.
- //b.If targetDesc.[[Configurable]] is false, then throw a TypeError exception.
- //c.Let extensibleTarget be the result of IsExtensible(target).
- //d.ReturnIfAbrupt(extensibleTarget).
- //e.If ToBoolean(extensibleTarget) is false, then throw a TypeError exception.
- //f.Return undefined.
- if (getResultTypeId == TypeIds_Undefined)
- {
- if (!hasProperty)
- {
- return FALSE;
- }
- if (!targetDescriptor.IsConfigurable())
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getOwnPropertyDescriptor"));
- }
- // do not use "target" here, the trap may have caused it to change
- if (!targetObj->IsExtensible())
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getOwnPropertyDescriptor"));
- }
- return FALSE;
- }
- //14. Let extensibleTarget be the result of IsExtensible(target).
- //15. ReturnIfAbrupt(extensibleTarget).
- //16. Let resultDesc be ToPropertyDescriptor(trapResultObj).
- //17. ReturnIfAbrupt(resultDesc).
- //18. Call CompletePropertyDescriptor(resultDesc, targetDesc).
- //19. Let valid be the result of IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc).
- //20. If valid is false, then throw a TypeError exception.
- //21. If resultDesc.[[Configurable]] is false, then
- //a.If targetDesc is undefined or targetDesc.[[Configurable]] is true, then
- //i.Throw a TypeError exception.
- //22. Return resultDesc.
- // do not use "target" here, the trap may have caused it to change
- BOOL isTargetExtensible = targetObj->IsExtensible();
- BOOL toProperty = JavascriptOperators::ToPropertyDescriptor(getResult, resultDescriptor, requestContext);
- if (!toProperty && isTargetExtensible)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getOwnPropertyDescriptor"));
- }
- JavascriptOperators::CompletePropertyDescriptor(resultDescriptor, nullptr, requestContext);
- if (!JavascriptOperators::IsCompatiblePropertyDescriptor(*resultDescriptor, hasProperty ? &targetDescriptor : nullptr, !!isTargetExtensible, true, requestContext))
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getOwnPropertyDescriptor"));
- }
- if (!resultDescriptor->IsConfigurable())
- {
- if (!hasProperty || targetDescriptor.IsConfigurable())
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getOwnPropertyDescriptor"));
- }
- }
- resultDescriptor->SetFromProxy(true);
- return toProperty;
- }
- template <class Fn, class GetPropertyIdFunc>
- BOOL JavascriptProxy::GetPropertyTrap(Var instance, PropertyDescriptor* propertyDescriptor, Fn fn, GetPropertyIdFunc getPropertyId, ScriptContext* requestContext)
- {
- PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
- // Reject implicit call
- ThreadContext* threadContext = requestContext->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return FALSE;
- }
- Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
- if (handlerObj == nullptr)
- {
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return FALSE;
- JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("get"));
- }
- RecyclableObject *targetObj = this->MarshalTarget(requestContext);
- JavascriptFunction* getGetMethod = GetMethodHelper(PropertyIds::get, requestContext);
- if (nullptr == getGetMethod || requestContext->IsHeapEnumInProgress())
- {
- propertyDescriptor->SetFromProxy(false);
- return fn(targetObj);
- }
- PropertyId propertyId = getPropertyId();
- propertyDescriptor->SetFromProxy(true);
- Var propertyName = GetName(requestContext, propertyId);
- Var getGetResult = threadContext->ExecuteImplicitCall(getGetMethod, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, getGetMethod, CallInfo(CallFlags_Value, 4), handlerObj, targetObj, propertyName, instance);
- });
- // 9. Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
- // 10. ReturnIfAbrupt(targetDesc).
- // 11. If targetDesc is not undefined, then
- // a.If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is false, then
- // i.If SameValue(trapResult, targetDesc.[[Value]]) is false, then throw a TypeError exception.
- // b.If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Get]] is undefined, then
- // i.If trapResult is not undefined, then throw a TypeError exception.
- // 12. Return trapResult.
- PropertyDescriptor targetDescriptor;
- Var defaultAccessor = requestContext->GetLibrary()->GetDefaultAccessorFunction();
- if (JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetDescriptor))
- {
- JavascriptOperators::CompletePropertyDescriptor(&targetDescriptor, nullptr, requestContext);
- if (targetDescriptor.ValueSpecified() && !targetDescriptor.IsConfigurable() && !targetDescriptor.IsWritable())
- {
- if (!JavascriptConversion::SameValue(getGetResult, targetDescriptor.GetValue()))
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("get"));
- }
- }
- else if (targetDescriptor.GetterSpecified() || targetDescriptor.SetterSpecified())
- {
- if (!targetDescriptor.IsConfigurable() &&
- targetDescriptor.GetGetter() == defaultAccessor &&
- JavascriptOperators::GetTypeId(getGetResult) != TypeIds_Undefined)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("get"));
- }
- }
- }
- propertyDescriptor->SetValue(getGetResult);
- return TRUE;
- }
- template <class Fn, class GetPropertyIdFunc>
- BOOL JavascriptProxy::HasPropertyTrap(Fn fn, GetPropertyIdFunc getPropertyId)
- {
- PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
- // Reject implicit call
- ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return FALSE;
- }
- // Caller does not pass requestContext. Retrieve from host scriptContext stack.
- ScriptContext* requestContext =
- threadContext->GetPreviousHostScriptContext()->GetScriptContext();
- Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
- if (handlerObj == nullptr)
- {
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return FALSE;
- JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("has"));
- }
- Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
- JavascriptFunction* hasMethod = GetMethodHelper(PropertyIds::has, requestContext);
- if (nullptr == hasMethod || requestContext->IsHeapEnumInProgress())
- {
- return fn(targetObj);
- }
- PropertyId propertyId = getPropertyId();
- Var propertyName = GetName(requestContext, propertyId);
- Var getHasResult = threadContext->ExecuteImplicitCall(hasMethod, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, hasMethod, CallInfo(CallFlags_Value, 3), handlerObj, targetObj, propertyName);
- });
- //9. Let booleanTrapResult be ToBoolean(trapResult).
- //10. ReturnIfAbrupt(booleanTrapResult).
- //11. If booleanTrapResult is false, then
- // a.Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
- // b.ReturnIfAbrupt(targetDesc).
- // c.If targetDesc is not undefined, then
- // i.If targetDesc.[[Configurable]] is false, then throw a TypeError exception.
- // ii.Let extensibleTarget be the result of IsExtensible(target).
- // iii.ReturnIfAbrupt(extensibleTarget).
- // iv.If ToBoolean(extensibleTarget) is false, then throw a TypeError exception
- BOOL hasProperty = JavascriptConversion::ToBoolean(getHasResult, requestContext);
- if (!hasProperty)
- {
- PropertyDescriptor targetDescriptor;
- BOOL hasTargetProperty = JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetDescriptor);
- if (hasTargetProperty)
- {
- if (!targetDescriptor.IsConfigurable() || !targetObj->IsExtensible())
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("has"));
- }
- }
- }
- return hasProperty;
- }
- PropertyQueryFlags JavascriptProxy::HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info)
- {
- if (info)
- {
- // Prevent caching. See comment in GetPropertyQuery for more detail.
- PropertyValueInfo::SetNoCache(info, this);
- PropertyValueInfo::DisablePrototypeCache(info, this);
- }
- auto fn = [&](RecyclableObject* object)->BOOL {
- return JavascriptOperators::HasProperty(object, propertyId);
- };
- auto getPropertyId = [&]() ->PropertyId {
- return propertyId;
- };
- return JavascriptConversion::BooleanToPropertyQueryFlags(HasPropertyTrap(fn, getPropertyId));
- }
- BOOL JavascriptProxy::HasOwnProperty(PropertyId propertyId)
- {
- // should never come here and it will be redirected to GetOwnPropertyDescriptor
- Assert(FALSE);
- PropertyDescriptor propertyDesc;
- return GetOwnPropertyDescriptor(this, propertyId, GetScriptContext(), &propertyDesc);
- }
- BOOL JavascriptProxy::HasOwnPropertyNoHostObject(PropertyId propertyId)
- {
- // the virtual method is for checking if globalobject has local property before we start initializing
- // we shouldn't trap??
- Assert(FALSE);
- return HasProperty(propertyId);
- }
- BOOL JavascriptProxy::HasOwnPropertyCheckNoRedecl(PropertyId propertyId)
- {
- // root object and activation object verification only; not needed.
- Assert(FALSE);
- return false;
- }
- BOOL JavascriptProxy::UseDynamicObjectForNoHostObjectAccess()
- {
- // heapenum check for CEO etc., and we don't want to access external method during enumeration. not applicable here.
- Assert(FALSE);
- return false;
- }
- DescriptorFlags JavascriptProxy::GetSetter(PropertyId propertyId, Var* setterValueOrProxy, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- // This is called when we walk prototype chain looking for setter. It is part of the [[set]] operation, but we don't need to restrict the
- // code to mimic the 'one step prototype chain lookup' spec letter. Current code structure is enough.
- *setterValueOrProxy = this;
- PropertyValueInfo::SetNoCache(info, this);
- PropertyValueInfo::DisablePrototypeCache(info, this); // We can't cache prototype property either
- return DescriptorFlags::Proxy;
- }
- // GetSetter is called for
- DescriptorFlags JavascriptProxy::GetSetter(JavascriptString* propertyNameString, Var* setterValueOrProxy, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- *setterValueOrProxy = this;
- PropertyValueInfo::SetNoCache(info, this);
- PropertyValueInfo::DisablePrototypeCache(info, this); // We can't cache prototype property either
- return DescriptorFlags::Proxy;
- }
- PropertyQueryFlags JavascriptProxy::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- // We can't cache the property at this time. both target and handler can be changed outside of the proxy, so the inline cache needs to be
- // invalidate when target, handler, or handler prototype has changed. We don't have a way to achieve this yet.
- // Also, Get and Has operations share a cache, so a trap on either should prevent caching on both.
- PropertyValueInfo::SetNoCache(info, this);
- PropertyValueInfo::DisablePrototypeCache(info, this); // We can't cache prototype property either
- auto fn = [&](RecyclableObject* object)-> BOOL {
- return JavascriptOperators::GetProperty(originalInstance, object, propertyId, value, requestContext, nullptr);
- };
- auto getPropertyId = [&]()->PropertyId {return propertyId; };
- PropertyDescriptor result;
- BOOL foundProperty = GetPropertyTrap(originalInstance, &result, fn, getPropertyId, requestContext);
- if (!foundProperty)
- {
- *value = requestContext->GetMissingPropertyResult();
- }
- else if (result.IsFromProxy())
- {
- *value = GetValueFromDescriptor(originalInstance, result, requestContext);
- }
- return JavascriptConversion::BooleanToPropertyQueryFlags(foundProperty);
- }
- PropertyQueryFlags JavascriptProxy::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- // We can't cache the property at this time. both target and handler can be changed outside of the proxy, so the inline cache needs to be
- // invalidate when target, handler, or handler prototype has changed. We don't have a way to achieve this yet.
- PropertyValueInfo::SetNoCache(info, this);
- PropertyValueInfo::DisablePrototypeCache(info, this); // We can't cache prototype property either
- auto fn = [&](RecyclableObject* object)-> BOOL {
- return JavascriptOperators::GetPropertyWPCache<false /* OutputExistence */>(originalInstance, object, propertyNameString, value, requestContext, info);
- };
- auto getPropertyId = [&]()->PropertyId{
- const PropertyRecord* propertyRecord;
- requestContext->GetOrAddPropertyRecord(propertyNameString, &propertyRecord);
- return propertyRecord->GetPropertyId();
- };
- PropertyDescriptor result;
- BOOL foundProperty = GetPropertyTrap(originalInstance, &result, fn, getPropertyId, requestContext);
- if (!foundProperty)
- {
- *value = requestContext->GetMissingPropertyResult();
- }
- else if (result.IsFromProxy())
- {
- *value = GetValueFromDescriptor(originalInstance, result, requestContext);
- }
- return JavascriptConversion::BooleanToPropertyQueryFlags(foundProperty);
- }
- BOOL JavascriptProxy::GetInternalProperty(Var instance, PropertyId internalPropertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- if (internalPropertyId == InternalPropertyIds::WeakMapKeyMap)
- {
- return __super::GetInternalProperty(instance, internalPropertyId, value, info, requestContext);
- }
- return FALSE;
- }
-
- _Check_return_ _Success_(return) BOOL JavascriptProxy::GetAccessors(PropertyId propertyId, _Outptr_result_maybenull_ Var* getter, _Outptr_result_maybenull_ Var* setter, ScriptContext* requestContext)
- {
- PropertyDescriptor result;
- if (getter != nullptr)
- {
- *getter = nullptr;
- }
- if (setter != nullptr)
- {
- *setter = nullptr;
- }
- BOOL foundProperty = GetOwnPropertyDescriptor(this, propertyId, requestContext, &result);
- if (foundProperty && result.IsFromProxy())
- {
- if (result.GetterSpecified() && getter != nullptr)
- {
- *getter = result.GetGetter();
- }
- if (result.SetterSpecified() && setter != nullptr)
- {
- *setter = result.GetSetter();
- }
- foundProperty = result.GetterSpecified() || result.SetterSpecified();
- }
- return foundProperty;
- }
- PropertyQueryFlags JavascriptProxy::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- // We can't cache the property at this time. both target and handler can be changed outside of the proxy, so the inline cache needs to be
- // invalidate when target, handler, or handler prototype has changed. We don't have a way to achieve this yet.
- PropertyValueInfo::SetNoCache(info, this);
- PropertyValueInfo::DisablePrototypeCache(info, this); // We can't cache prototype property either
- auto fn = [&](RecyclableObject* object)-> BOOL {
- return JavascriptOperators::GetPropertyReference(originalInstance, object, propertyId, value, requestContext, nullptr);
- };
- auto getPropertyId = [&]() -> PropertyId {return propertyId; };
- PropertyDescriptor result;
- BOOL foundProperty = GetPropertyTrap(originalInstance, &result, fn, getPropertyId, requestContext);
- if (!foundProperty)
- {
- *value = requestContext->GetMissingPropertyResult();
- }
- else if (result.IsFromProxy())
- {
- *value = GetValueFromDescriptor(originalInstance, result, requestContext);
- }
- return JavascriptConversion::BooleanToPropertyQueryFlags(foundProperty);
- }
- BOOL JavascriptProxy::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
- {
- PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
- // This is the second half of [[set]] where when the handler does not specified [[set]] so we forward to [[set]] on target
- // with receiver as the proxy.
- //c.Let existingDescriptor be the result of calling the[[GetOwnProperty]] internal method of Receiver with argument P.
- //d.ReturnIfAbrupt(existingDescriptor).
- //e.If existingDescriptor is not undefined, then
- // i.Let valueDesc be the PropertyDescriptor{ [[Value]]: V }.
- // ii.Return the result of calling the[[DefineOwnProperty]] internal method of Receiver with arguments P and valueDesc.
- //f.Else Receiver does not currently have a property P,
- // i.Return the result of performing CreateDataProperty(Receiver, P, V).
- // We can't cache the property at this time. both target and handler can be changed outside of the proxy, so the inline cache needs to be
- // invalidate when target, handler, or handler prototype has changed. We don't have a way to achieve this yet.
- PropertyValueInfo::SetNoCache(info, this);
- PropertyValueInfo::DisablePrototypeCache(info, this); // We can't cache prototype property either
- PropertyDescriptor proxyPropertyDescriptor;
- ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
- ScriptContext* requestContext =
- threadContext->GetPreviousHostScriptContext()->GetScriptContext();
- // Set implicit call flag so we bailout and not do copy-prop on field
- Js::ImplicitCallFlags saveImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->SetImplicitCallFlags((Js::ImplicitCallFlags)(saveImplicitCallFlags | ImplicitCall_Accessor));
- if (!JavascriptOperators::GetOwnPropertyDescriptor(this, propertyId, requestContext, &proxyPropertyDescriptor))
- {
- PropertyDescriptor resultDescriptor;
- resultDescriptor.SetConfigurable(true);
- resultDescriptor.SetWritable(true);
- resultDescriptor.SetEnumerable(true);
- resultDescriptor.SetValue(value);
- return Js::JavascriptOperators::DefineOwnPropertyDescriptor(this, propertyId, resultDescriptor, true, requestContext);
- }
- else
- {
- // ES2017 Spec'd (9.1.9.1):
- // If existingDescriptor is not undefined, then
- // If IsAccessorDescriptor(existingDescriptor) is true, return false.
- // If existingDescriptor.[[Writable]] is false, return false.
- if (proxyPropertyDescriptor.IsAccessorDescriptor())
- {
- return FALSE;
- }
- if (proxyPropertyDescriptor.WritableSpecified() && !proxyPropertyDescriptor.IsWritable())
- {
- return FALSE;
- }
- proxyPropertyDescriptor.SetValue(value);
- proxyPropertyDescriptor.SetOriginal(nullptr);
- return Js::JavascriptOperators::DefineOwnPropertyDescriptor(this, propertyId, proxyPropertyDescriptor, true, requestContext);
- }
- }
- BOOL JavascriptProxy::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
- {
- const PropertyRecord* propertyRecord;
- GetScriptContext()->GetOrAddPropertyRecord(propertyNameString, &propertyRecord);
- return SetProperty(propertyRecord->GetPropertyId(), value, flags, info);
- }
- BOOL JavascriptProxy::SetInternalProperty(PropertyId internalPropertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
- {
- if (internalPropertyId == InternalPropertyIds::WeakMapKeyMap)
- {
- return __super::SetInternalProperty(internalPropertyId, value, flags, info);
- }
- return FALSE;
- }
- BOOL JavascriptProxy::InitProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
- {
- return SetProperty(propertyId, value, flags, info);
- }
- BOOL JavascriptProxy::EnsureProperty(PropertyId propertyId)
- {
- // proxy needs to be explicitly constructed. we don't have Ensure code path.
- Assert(FALSE);
- return false;
- }
- BOOL JavascriptProxy::EnsureNoRedeclProperty(PropertyId propertyId)
- {
- // proxy needs to be explicitly constructed. we don't have Ensure code path.
- Assert(FALSE);
- return false;
- }
- BOOL JavascriptProxy::SetPropertyWithAttributes(PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects)
- {
- // called from untrapped DefineProperty and from DOM side. I don't see this being used when the object is a proxy.
- Assert(FALSE);
- return false;
- }
- BOOL JavascriptProxy::InitPropertyScoped(PropertyId propertyId, Var value)
- {
- // proxy needs to be explicitly constructed. we don't have Ensure code path.
- Assert(FALSE);
- return false;
- }
- BOOL JavascriptProxy::InitFuncScoped(PropertyId propertyId, Var value)
- {
- // proxy needs to be explicitly constructed. we don't have Ensure code path.
- Assert(FALSE);
- return false;
- }
- BOOL JavascriptProxy::DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags)
- {
- PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
- // Reject implicit call
- ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return FALSE;
- }
- // Caller does not pass requestContext. Retrieve from host scriptContext stack.
- ScriptContext* requestContext =
- threadContext->GetPreviousHostScriptContext()->GetScriptContext();
- //1. Assert: IsPropertyKey(P) is true.
- //2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
- RecyclableObject * handlerObj = this->MarshalHandler(requestContext);
- //3. If handler is null, then throw a TypeError exception.
- //6. ReturnIfAbrupt(trap).
- if (handlerObj == nullptr)
- {
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return FALSE;
- JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("deleteProperty"));
- }
- //4. Let target be the value of the[[ProxyTarget]] internal slot of O.
- RecyclableObject * targetObj = this->MarshalTarget(requestContext);
- //5. Let trap be the result of GetMethod(handler, "deleteProperty").
- JavascriptFunction* deleteMethod = GetMethodHelper(PropertyIds::deleteProperty, requestContext);
- //7. If trap is undefined, then
- //a.Return the result of calling the[[Delete]] internal method of target with argument P.
- Assert(!GetScriptContext()->IsHeapEnumInProgress());
- if (nullptr == deleteMethod)
- {
- uint32 indexVal;
- if (requestContext->IsNumericPropertyId(propertyId, &indexVal))
- {
- return targetObj->DeleteItem(indexVal, flags);
- }
- else
- {
- return targetObj->DeleteProperty(propertyId, flags);
- }
- }
- //8. Let trapResult be the result of calling the[[Call]] internal method of trap with handler as the this value and a new List containing target and P.
- //9. Let booleanTrapResult be ToBoolean(trapResult).
- //10. ReturnIfAbrupt(booleanTrapResult).
- //11. If booleanTrapResult is false, then return false.
- Var propertyName = GetName(requestContext, propertyId);
- Var deletePropertyResult = threadContext->ExecuteImplicitCall(deleteMethod, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, deleteMethod, CallInfo(CallFlags_Value, 3), handlerObj, targetObj, propertyName);
- });
- BOOL trapResult = JavascriptConversion::ToBoolean(deletePropertyResult, requestContext);
- if (!trapResult)
- {
- return trapResult;
- }
- //12. Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
- //13. ReturnIfAbrupt(targetDesc).
- //14. If targetDesc is undefined, then return true.
- //15. If targetDesc.[[Configurable]] is false, then throw a TypeError exception.
- //16. Return true.
- PropertyDescriptor targetPropertyDescriptor;
- if (!Js::JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetPropertyDescriptor))
- {
- return TRUE;
- }
- if (!targetPropertyDescriptor.IsConfigurable())
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("deleteProperty"));
- }
- return TRUE;
- }
- BOOL JavascriptProxy::DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags)
- {
- PropertyRecord const *propertyRecord = nullptr;
- if (JavascriptOperators::ShouldTryDeleteProperty(this, propertyNameString, &propertyRecord))
- {
- Assert(propertyRecord);
- return DeleteProperty(propertyRecord->GetPropertyId(), flags);
- }
- return TRUE;
- }
- #if ENABLE_FIXED_FIELDS
- BOOL JavascriptProxy::IsFixedProperty(PropertyId propertyId)
- {
- // TODO: can we add support for fixed property? don't see a clear way to invalidate...
- return false;
- }
- #endif
- PropertyQueryFlags JavascriptProxy::HasItemQuery(uint32 index)
- {
- const PropertyRecord* propertyRecord;
- auto fn = [&](RecyclableObject* object)-> BOOL {
- return JavascriptOperators::HasItem(object, index);
- };
- auto getPropertyId = [&]() ->PropertyId {
- PropertyIdFromInt(index, &propertyRecord);
- return propertyRecord->GetPropertyId();
- };
- return JavascriptConversion::BooleanToPropertyQueryFlags(HasPropertyTrap(fn, getPropertyId));
- }
- BOOL JavascriptProxy::HasOwnItem(uint32 index)
- {
- const PropertyRecord* propertyRecord;
- auto fn = [&](RecyclableObject* object)-> BOOL {
- return JavascriptOperators::HasOwnItem(object, index);
- };
- auto getPropertyId = [&]() ->PropertyId {
- PropertyIdFromInt(index, &propertyRecord);
- return propertyRecord->GetPropertyId();
- };
- return HasPropertyTrap(fn, getPropertyId);
- }
- PropertyQueryFlags JavascriptProxy::GetItemQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext)
- {
- const PropertyRecord* propertyRecord;
- auto fn = [&](RecyclableObject* object)-> BOOL {
- return JavascriptOperators::GetItem(originalInstance, object, index, value, requestContext);
- };
- auto getPropertyId = [&]() ->PropertyId {
- PropertyIdFromInt(index, &propertyRecord);
- return propertyRecord->GetPropertyId();
- };
- PropertyDescriptor result;
- BOOL foundProperty = GetPropertyTrap(originalInstance, &result, fn, getPropertyId, requestContext);
- if (!foundProperty)
- {
- *value = requestContext->GetMissingItemResult();
- }
- else if (result.IsFromProxy())
- {
- *value = GetValueFromDescriptor(originalInstance, result, requestContext);
- }
- return JavascriptConversion::BooleanToPropertyQueryFlags(foundProperty);
- }
- PropertyQueryFlags JavascriptProxy::GetItemReferenceQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext)
- {
- const PropertyRecord* propertyRecord;
- auto fn = [&](RecyclableObject* object)-> BOOL {
- return JavascriptOperators::GetItem(originalInstance, object, index, value, requestContext);
- };
- auto getPropertyId = [&]() ->PropertyId {
- PropertyIdFromInt(index, &propertyRecord);
- return propertyRecord->GetPropertyId();
- };
- PropertyDescriptor result;
- BOOL foundProperty = GetPropertyTrap(originalInstance, &result, fn, getPropertyId, requestContext);
- if (!foundProperty)
- {
- *value = requestContext->GetMissingItemResult();
- }
- else if (result.IsFromProxy())
- {
- *value = GetValueFromDescriptor(originalInstance, result, requestContext);
- }
- return JavascriptConversion::BooleanToPropertyQueryFlags(foundProperty);
- }
- DescriptorFlags JavascriptProxy::GetItemSetter(uint32 index, Var* setterValueOrProxy, ScriptContext* requestContext)
- {
- *setterValueOrProxy = this;
- return DescriptorFlags::Proxy;
- }
- BOOL JavascriptProxy::SetItem(uint32 index, Var value, PropertyOperationFlags flags)
- {
- const PropertyRecord* propertyRecord;
- PropertyIdFromInt(index, &propertyRecord);
- return SetProperty(propertyRecord->GetPropertyId(), value, flags, nullptr);
- }
- BOOL JavascriptProxy::DeleteItem(uint32 index, PropertyOperationFlags flags)
- {
- const PropertyRecord* propertyRecord;
- PropertyIdFromInt(index, &propertyRecord);
- return DeleteProperty(propertyRecord->GetPropertyId(), flags);
- }
- // No change to foreign enumerator, just forward
- BOOL JavascriptProxy::GetEnumerator(JavascriptStaticEnumerator * enumerator, EnumeratorFlags flags, ScriptContext* requestContext, EnumeratorCache * enumeratorCache)
- {
- // Reject implicit call
- ThreadContext* threadContext = requestContext->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return FALSE;
- }
- // 1. Assert: Either Type(V) is Object or Type(V) is Null.
- // 2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
- // 3. If handler is null, then throw a TypeError exception.
- if (this->handler == nullptr)
- {
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return FALSE;
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("ownKeys"));
- }
- struct ProxyOwnkeysEnumerator : public JavascriptEnumerator
- {
- typedef JsUtil::BaseHashSet<const char16*, Recycler> VisitedNamesHashSet;
- Field(VisitedNamesHashSet*) visited;
- Field(JavascriptArray*) trapResult;
- Field(JavascriptProxy*) proxy;
- FieldNoBarrier(ScriptContext*) scriptContext;
- Field(uint32) index;
- DEFINE_VTABLE_CTOR_ABSTRACT(ProxyOwnkeysEnumerator, JavascriptEnumerator)
- ProxyOwnkeysEnumerator(ScriptContext* scriptContext, JavascriptProxy* proxy, JavascriptArray* trapResult)
- :JavascriptEnumerator(scriptContext), scriptContext(scriptContext), proxy(proxy), trapResult(trapResult)
- {
- visited = RecyclerNew(scriptContext->GetRecycler(), VisitedNamesHashSet, scriptContext->GetRecycler());
- }
- virtual void Reset() override
- {
- index = 0;
- visited->Reset();
- }
- virtual JavascriptString * MoveAndGetNext(PropertyId& propertyId, PropertyAttributes* attributes = nullptr) override
- {
- propertyId = Constants::NoProperty;
- if (attributes != nullptr)
- {
- *attributes = PropertyEnumerable;
- }
- // 13.7.5.15 EnumerateObjectProperties(O) (https://tc39.github.io/ecma262/#sec-enumerate-object-properties)
- // for (let key of Reflect.ownKeys(obj)) {
- uint32 len = trapResult->GetLength();
- while (index < len)
- {
- Var var = trapResult->DirectGetItem(index++) ;
- if (var)
- {
- // if (typeof key === "string") {
- if (JavascriptString::Is(var))
- {
- JavascriptString* propertyName = JavascriptString::FromVar(var);
- // let desc = Reflect.getOwnPropertyDescriptor(obj, key);
- Js::PropertyDescriptor desc;
- BOOL ret = JavascriptOperators::GetOwnPropertyDescriptor(proxy, propertyName, scriptContext, &desc);
- // if (desc && !visited.has(key)) {
- if (ret && !visited->Contains(propertyName->GetSz()))
- {
- visited->Add(propertyName->GetSz());
- // if (desc.enumerable) yield key;
- if (desc.IsEnumerable())
- {
- return JavascriptString::FromVar(CrossSite::MarshalVar(
- scriptContext, propertyName, propertyName->GetScriptContext()));
- }
- }
- }
- }
- }
- return nullptr;
- }
- };
- JavascriptArray* trapResult = JavascriptOperators::GetOwnPropertyNames(this, requestContext);
- ProxyOwnkeysEnumerator* ownKeysEnum = RecyclerNew(requestContext->GetRecycler(), ProxyOwnkeysEnumerator, requestContext, this, trapResult);
- return enumerator->Initialize(ownKeysEnum, nullptr, nullptr, flags, requestContext, enumeratorCache);
- }
- BOOL JavascriptProxy::SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags)
- {
- // should be for __definegetter style usage. need to wait for clear spec what it means.
- Assert(FALSE);
- return false;
- }
- BOOL JavascriptProxy::Equals(__in Var other, __out BOOL* value, ScriptContext* requestContext)
- {
- //RecyclableObject* targetObj;
- if (this->target == nullptr)
- {
- // the proxy has been revoked; TypeError.
- JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("equal"));
- }
- // Reject implicit call
- ThreadContext* threadContext = requestContext->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- *value = FALSE;
- return FALSE;
- }
- *value = (other == this);
- return true;
- }
- BOOL JavascriptProxy::StrictEquals(__in Var other, __out BOOL* value, ScriptContext* requestContext)
- {
- *value = FALSE;
- //RecyclableObject* targetObj;
- if (this->target == nullptr)
- {
- // the proxy has been revoked; TypeError.
- JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("strict equal"));
- }
- // Reject implicit call
- ThreadContext* threadContext = requestContext->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return FALSE;
- }
- *value = (other == this);
- return true;
- }
- BOOL JavascriptProxy::IsWritable(PropertyId propertyId)
- {
- PropertyDescriptor propertyDescriptor;
- if (!GetOwnPropertyDescriptor(this, propertyId, GetScriptContext(), &propertyDescriptor))
- {
- return FALSE;
- }
- // If property descriptor has getter/setter we should check if writable is specified before checking IsWritable
- return propertyDescriptor.WritableSpecified() ? propertyDescriptor.IsWritable() : FALSE;
- }
- BOOL JavascriptProxy::IsConfigurable(PropertyId propertyId)
- {
- Assert(FALSE);
- return target->IsConfigurable(propertyId);
- }
- BOOL JavascriptProxy::IsEnumerable(PropertyId propertyId)
- {
- Assert(FALSE);
- return target->IsEnumerable(propertyId);
- }
- BOOL JavascriptProxy::IsExtensible()
- {
- PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
- // Reject implicit call
- ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return FALSE;
- }
- // Caller does not pass requestContext. Retrieve from host scriptContext stack.
- ScriptContext* requestContext =
- threadContext->GetPreviousHostScriptContext()->GetScriptContext();
- //1. Let handler be the value of the[[ProxyHandler]] internal slot of O.
- Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
- //2. If handler is null, then throw a TypeError exception.
- if (handlerObj == nullptr)
- {
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return FALSE;
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("isExtensible"));
- }
- //3. Let target be the value of the[[ProxyTarget]] internal slot of O.
- Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
- //4. Let trap be the result of GetMethod(handler, "isExtensible").
- //5. ReturnIfAbrupt(trap).
- //6. If trap is undefined, then
- //a.Return the result of calling the[[IsExtensible]] internal method of target.
- //7. Let trapResult be the result of calling the[[Call]] internal method of trap with handler as the this value and a new List containing target.
- //8. Let booleanTrapResult be ToBoolean(trapResult).
- //9. ReturnIfAbrupt(booleanTrapResult).
- //10. Let targetResult be the result of calling the[[IsExtensible]] internal method of target.
- //11. ReturnIfAbrupt(targetResult).
- //12. If SameValue(booleanTrapResult, targetResult) is false, then throw a TypeError exception.
- //13. Return booleanTrapResult.
- JavascriptFunction* isExtensibleMethod = GetMethodHelper(PropertyIds::isExtensible, requestContext);
- Assert(!requestContext->IsHeapEnumInProgress());
- if (nullptr == isExtensibleMethod)
- {
- return targetObj->IsExtensible();
- }
-
- Var isExtensibleResult = threadContext->ExecuteImplicitCall(isExtensibleMethod, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, isExtensibleMethod, CallInfo(CallFlags_Value, 2), handlerObj, targetObj);
- });
-
- BOOL trapResult = JavascriptConversion::ToBoolean(isExtensibleResult, requestContext);
- BOOL targetIsExtensible = targetObj->IsExtensible();
- if (trapResult != targetIsExtensible)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("isExtensible"));
- }
- return trapResult;
- }
- BOOL JavascriptProxy::PreventExtensions()
- {
- PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
- // Reject implicit call
- ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return FALSE;
- }
- // Caller does not pass requestContext. Retrieve from host scriptContext stack.
- ScriptContext* requestContext =
- threadContext->GetPreviousHostScriptContext()->GetScriptContext();
- //1. Let handler be the value of the[[ProxyHandler]] internal slot of O.
- Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
- //2. If handler is null, then throw a TypeError exception.
- if (handlerObj == nullptr)
- {
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return FALSE;
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("preventExtensions"));
- }
- //3. Let target be the value of the[[ProxyTarget]] internal slot of O.
- Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
- //4. Let trap be the result of GetMethod(handler, "preventExtensions").
- //5. ReturnIfAbrupt(trap).
- //6. If trap is undefined, then
- //a.Return the result of calling the[[PreventExtensions]] internal method of target.
- //7. Let trapResult be the result of calling the[[Call]] internal method of trap with handler as the this value and a new List containing target.
- JavascriptFunction* preventExtensionsMethod = GetMethodHelper(PropertyIds::preventExtensions, requestContext);
- Assert(!GetScriptContext()->IsHeapEnumInProgress());
- if (nullptr == preventExtensionsMethod)
- {
- return targetObj->PreventExtensions();
- }
-
- //8. Let booleanTrapResult be ToBoolean(trapResult)
- //9. ReturnIfAbrupt(booleanTrapResult).
- //10. Let targetIsExtensible be the result of calling the[[IsExtensible]] internal method of target.
- //11. ReturnIfAbrupt(targetIsExtensible).
- //12. If booleanTrapResult is true and targetIsExtensible is true, then throw a TypeError exception.
- //13. Return booleanTrapResult.
- Var preventExtensionsResult = threadContext->ExecuteImplicitCall(preventExtensionsMethod, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, preventExtensionsMethod, CallInfo(CallFlags_Value, 2), handlerObj, targetObj);
- });
- BOOL trapResult = JavascriptConversion::ToBoolean(preventExtensionsResult, requestContext);
- if (trapResult)
- {
- BOOL targetIsExtensible = targetObj->IsExtensible();
- if (targetIsExtensible)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("preventExtensions"));
- }
- }
- return trapResult;
- }
- // 7.3.12 in ES 2015. While this should have been no observable behavior change. Till there is obvious change warrant this
- // to be moved to JavascriptOperators, let's keep it in proxy only first.
- BOOL JavascriptProxy::TestIntegrityLevel(IntegrityLevel integrityLevel, RecyclableObject* obj, ScriptContext* scriptContext)
- {
- //1. Assert: Type(O) is Object.
- //2. Assert: level is either "sealed" or "frozen".
- //3. Let status be IsExtensible(O).
- //4. ReturnIfAbrupt(status).
- //5. If status is true, then return false
- //6. NOTE If the object is extensible, none of its properties are examined.
- BOOL isExtensible = obj->IsExtensible();
- if (isExtensible)
- {
- return FALSE;
- }
- // at this time this is called from proxy only; when we extend this to other objects, we need to handle the other codepath.
- //7. Let keys be O.[[OwnPropertyKeys]]().
- //8. ReturnIfAbrupt(keys).
- Assert(JavascriptProxy::Is(obj));
- JavascriptArray* resultArray = JavascriptOperators::GetOwnPropertyKeys(obj, scriptContext);
- //9. Repeat for each element k of keys,
- // a. Let currentDesc be O.[[GetOwnProperty]](k).
- // b. ReturnIfAbrupt(currentDesc).
- // c. If currentDesc is not undefined, then
- // i. If currentDesc.[[Configurable]] is true, return false.
- // ii. If level is "frozen" and IsDataDescriptor(currentDesc) is true, then
- // 1. If currentDesc.[[Writable]] is true, return false.
- Var itemVar;
- bool writable = false;
- bool configurable = false;
- const PropertyRecord* propertyRecord;
- PropertyDescriptor propertyDescriptor;
- for (uint i = 0; i < resultArray->GetLength(); i++)
- {
- itemVar = resultArray->DirectGetItem(i);
- AssertMsg(JavascriptSymbol::Is(itemVar) || JavascriptString::Is(itemVar), "Invariant check during ownKeys proxy trap should make sure we only get property key here. (symbol or string primitives)");
- JavascriptConversion::ToPropertyKey(itemVar, scriptContext, &propertyRecord, nullptr);
- PropertyId propertyId = propertyRecord->GetPropertyId();
- if (JavascriptObject::GetOwnPropertyDescriptorHelper(obj, propertyId, scriptContext, propertyDescriptor))
- {
- configurable |= propertyDescriptor.IsConfigurable();
- if (propertyDescriptor.IsDataDescriptor())
- {
- writable |= propertyDescriptor.IsWritable();
- }
- }
- }
- if (integrityLevel == IntegrityLevel::IntegrityLevel_frozen && writable)
- {
- return FALSE;
- }
- if (configurable)
- {
- return FALSE;
- }
- return TRUE;
- }
- BOOL JavascriptProxy::SetIntegrityLevel(IntegrityLevel integrityLevel, RecyclableObject* obj, ScriptContext* scriptContext)
- {
- //1. Assert: Type(O) is Object.
- //2. Assert : level is either "sealed" or "frozen".
- //3. Let status be O.[[PreventExtensions]]().
- //4. ReturnIfAbrupt(status).
- //5. If status is false, return false.
- // at this time this is called from proxy only; when we extend this to other objects, we need to handle the other codepath.
- Assert(JavascriptProxy::Is(obj));
- if (obj->PreventExtensions() == FALSE)
- return FALSE;
- //6. Let keys be O.[[OwnPropertyKeys]]().
- //7. ReturnIfAbrupt(keys).
- JavascriptArray* resultArray = JavascriptOperators::GetOwnPropertyKeys(obj, scriptContext);
- const PropertyRecord* propertyRecord;
- if (integrityLevel == IntegrityLevel::IntegrityLevel_sealed)
- {
- //8. If level is "sealed", then
- //a. Repeat for each element k of keys,
- //i. Let status be DefinePropertyOrThrow(O, k, PropertyDescriptor{ [[Configurable]]: false }).
- //ii. ReturnIfAbrupt(status).
- PropertyDescriptor propertyDescriptor;
- propertyDescriptor.SetConfigurable(false);
- Var itemVar;
- for (uint i = 0; i < resultArray->GetLength(); i++)
- {
- itemVar = resultArray->DirectGetItem(i);
- AssertMsg(JavascriptSymbol::Is(itemVar) || JavascriptString::Is(itemVar), "Invariant check during ownKeys proxy trap should make sure we only get property key here. (symbol or string primitives)");
- JavascriptConversion::ToPropertyKey(itemVar, scriptContext, &propertyRecord, nullptr);
- PropertyId propertyId = propertyRecord->GetPropertyId();
- JavascriptObject::DefineOwnPropertyHelper(obj, propertyId, propertyDescriptor, scriptContext);
- }
- }
- else
- {
- //9.Else level is "frozen",
- // a.Repeat for each element k of keys,
- // i. Let currentDesc be O.[[GetOwnProperty]](k).
- // ii. ReturnIfAbrupt(currentDesc).
- // iii. If currentDesc is not undefined, then
- // 1. If IsAccessorDescriptor(currentDesc) is true, then
- // a. Let desc be the PropertyDescriptor{[[Configurable]]: false}.
- // 2.Else,
- // a. Let desc be the PropertyDescriptor { [[Configurable]]: false, [[Writable]]: false }.
- // 3. Let status be DefinePropertyOrThrow(O, k, desc).
- // 4. ReturnIfAbrupt(status).
- Assert(integrityLevel == IntegrityLevel::IntegrityLevel_frozen);
- PropertyDescriptor current, dataDescriptor, accessorDescriptor;
- dataDescriptor.SetConfigurable(false);
- dataDescriptor.SetWritable(false);
- accessorDescriptor.SetConfigurable(false);
- Var itemVar;
- for (uint i = 0; i < resultArray->GetLength(); i++)
- {
- itemVar = resultArray->DirectGetItem(i);
- AssertMsg(JavascriptSymbol::Is(itemVar) || JavascriptString::Is(itemVar), "Invariant check during ownKeys proxy trap should make sure we only get property key here. (symbol or string primitives)");
- JavascriptConversion::ToPropertyKey(itemVar, scriptContext, &propertyRecord, nullptr);
- PropertyId propertyId = propertyRecord->GetPropertyId();
- PropertyDescriptor propertyDescriptor;
- if (JavascriptObject::GetOwnPropertyDescriptorHelper(obj, propertyId, scriptContext, propertyDescriptor))
- {
- if (propertyDescriptor.IsDataDescriptor())
- {
- JavascriptObject::DefineOwnPropertyHelper(obj, propertyRecord->GetPropertyId(), dataDescriptor, scriptContext);
- }
- else if (propertyDescriptor.IsAccessorDescriptor())
- {
- JavascriptObject::DefineOwnPropertyHelper(obj, propertyRecord->GetPropertyId(), accessorDescriptor, scriptContext);
- }
- }
- }
- }
- // 10. Return true
- return TRUE;
- }
- BOOL JavascriptProxy::Seal()
- {
- return SetIntegrityLevel(IntegrityLevel::IntegrityLevel_sealed, this, this->GetScriptContext());
- }
- BOOL JavascriptProxy::Freeze()
- {
- return SetIntegrityLevel(IntegrityLevel::IntegrityLevel_frozen, this, this->GetScriptContext());
- }
- BOOL JavascriptProxy::IsSealed()
- {
- return TestIntegrityLevel(IntegrityLevel::IntegrityLevel_sealed, this, this->GetScriptContext());
- }
- BOOL JavascriptProxy::IsFrozen()
- {
- return TestIntegrityLevel(IntegrityLevel::IntegrityLevel_frozen, this, this->GetScriptContext());
- }
- BOOL JavascriptProxy::SetWritable(PropertyId propertyId, BOOL value)
- {
- Assert(FALSE);
- return FALSE;
- }
- BOOL JavascriptProxy::SetConfigurable(PropertyId propertyId, BOOL value)
- {
- Assert(FALSE);
- return FALSE;
- }
- BOOL JavascriptProxy::SetEnumerable(PropertyId propertyId, BOOL value)
- {
- Assert(FALSE);
- return FALSE;
- }
- BOOL JavascriptProxy::SetAttributes(PropertyId propertyId, PropertyAttributes attributes)
- {
- Assert(FALSE);
- return FALSE;
- }
- BOOL JavascriptProxy::HasInstance(Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache)
- {
- Var funcPrototype = JavascriptOperators::GetProperty(this, PropertyIds::prototype, scriptContext);
- return JavascriptFunction::HasInstance(funcPrototype, instance, scriptContext, NULL, NULL);
- }
- JavascriptString* JavascriptProxy::GetClassName(ScriptContext * requestContext)
- {
- Assert(FALSE);
- return nullptr;
- }
- RecyclableObject* JavascriptProxy::GetPrototypeSpecial()
- {
- PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
- // Reject implicit call
- ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return nullptr;
- }
- // Caller does not pass requestContext. Retrieve from host scriptContext stack.
- ScriptContext* requestContext =
- threadContext->GetPreviousHostScriptContext()->GetScriptContext();
- Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
- if (handlerObj == nullptr)
- {
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return nullptr;
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("getPrototypeOf"));
- }
- Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
- JavascriptFunction* getPrototypeOfMethod = GetMethodHelper(PropertyIds::getPrototypeOf, requestContext);
- if (nullptr == getPrototypeOfMethod || GetScriptContext()->IsHeapEnumInProgress())
- {
- return RecyclableObject::FromVar(JavascriptObject::GetPrototypeOf(targetObj, requestContext));
- }
-
- Var getPrototypeOfResult = threadContext->ExecuteImplicitCall(getPrototypeOfMethod, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, getPrototypeOfMethod, CallInfo(CallFlags_Value, 2), handlerObj, targetObj);
- });
-
- TypeId prototypeTypeId = JavascriptOperators::GetTypeId(getPrototypeOfResult);
- if (!JavascriptOperators::IsObjectType(prototypeTypeId) && prototypeTypeId != TypeIds_Null)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getPrototypeOf"));
- }
- if (!targetObj->IsExtensible() && !JavascriptConversion::SameValue(getPrototypeOfResult, targetObj->GetPrototype()))
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getPrototypeOf"));
- }
- return RecyclableObject::FromVar(getPrototypeOfResult);
- }
- RecyclableObject* JavascriptProxy::GetConfigurablePrototype(ScriptContext * requestContext)
- {
- // We should be using GetPrototypeSpecial for proxy object; never should come over here.
- Assert(FALSE);
- return nullptr;
- }
- void JavascriptProxy::RemoveFromPrototype(ScriptContext * requestContext)
- {
- Assert(FALSE);
- }
- void JavascriptProxy::AddToPrototype(ScriptContext * requestContext)
- {
- Assert(FALSE);
- }
- void JavascriptProxy::SetPrototype(RecyclableObject* newPrototype)
- {
- Assert(FALSE);
- }
- BOOL JavascriptProxy::SetPrototypeTrap(RecyclableObject* newPrototype, bool shouldThrow,
- ScriptContext * requestContext)
- {
- PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
- Assert(JavascriptOperators::IsObjectOrNull(newPrototype));
- // Reject implicit call
- ThreadContext* threadContext = requestContext->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return FALSE;
- }
- //1. Assert: Either Type(V) is Object or Type(V) is Null.
- //2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
- Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
- //3. If handler is null, then throw a TypeError exception.
- if (handlerObj == nullptr)
- {
- // the proxy has been revoked; TypeError.
- if (shouldThrow)
- {
- if (!threadContext->RecordImplicitException())
- return FALSE;
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("setPrototypeOf"));
- }
- }
- //4. Let target be the value of the[[ProxyTarget]] internal slot of O.
- Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
- //5. Let trap be the result of GetMethod(handler, "setPrototypeOf").
- //6. ReturnIfAbrupt(trap).
- //7. If trap is undefined, then
- //a.Return the result of calling the[[SetPrototypeOf]] internal method of target with argument V.
- JavascriptFunction* setPrototypeOfMethod = GetMethodHelper(PropertyIds::setPrototypeOf, requestContext);
- Assert(!GetScriptContext()->IsHeapEnumInProgress());
- if (nullptr == setPrototypeOfMethod)
- {
- JavascriptObject::ChangePrototype(targetObj, newPrototype, shouldThrow, requestContext);
- return TRUE;
- }
- //8. Let trapResult be the result of calling the[[Call]] internal method of trap with handler as the this value and a new List containing target and V.
- Var setPrototypeResult = threadContext->ExecuteImplicitCall(setPrototypeOfMethod, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, setPrototypeOfMethod, CallInfo(CallFlags_Value, 3), handlerObj, targetObj, newPrototype);
- });
- //9. Let booleanTrapResult be ToBoolean(trapResult).
- //10. ReturnIfAbrupt(booleanTrapResult).
- //11. Let extensibleTarget be the result of IsExtensible(target).
- //12. ReturnIfAbrupt(extensibleTarget).
- //13. If extensibleTarget is true, then return booleanTrapResult.
- //14. Let targetProto be the result of calling the[[GetPrototypeOf]] internal method of target.
- //15. ReturnIfAbrupt(targetProto).
- //16. If booleanTrapResult is true and SameValue(V, targetProto) is false, then throw a TypeError exception.
- //17. Return booleanTrapResult.
- BOOL prototypeSetted = JavascriptConversion::ToBoolean(setPrototypeResult, requestContext);
- BOOL isExtensible = targetObj->IsExtensible();
- if (isExtensible)
- {
- if (!prototypeSetted && shouldThrow)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_ProxyTrapReturnedFalse, _u("setPrototypeOf"));
- }
- return prototypeSetted;
- }
- Var targetProto = targetObj->GetPrototype();
- if (!JavascriptConversion::SameValue(targetProto, newPrototype))
- {
- if (shouldThrow)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("setPrototypeOf"));
- }
- return FALSE;
- }
- return TRUE;
- }
- Var JavascriptProxy::ToString(ScriptContext* scriptContext)
- {
- //RecyclableObject* targetObj;
- if (this->handler == nullptr)
- {
- ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return nullptr;
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("toString"));
- }
- return JavascriptObject::ToStringHelper(target, scriptContext);
- }
- BOOL JavascriptProxy::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
- {
- //RecyclableObject* targetObj;
- if (this->handler == nullptr)
- {
- ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return FALSE;
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("getTypeString"));
- }
- return target->GetDiagTypeString(stringBuilder, requestContext);
- }
- RecyclableObject* JavascriptProxy::ToObject(ScriptContext * requestContext)
- {
- //RecyclableObject* targetObj;
- if (this->handler == nullptr)
- {
- ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return nullptr;
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("toObject"));
- }
- return __super::ToObject(requestContext);
- }
- Var JavascriptProxy::GetTypeOfString(ScriptContext* requestContext)
- {
- if (this->handler == nullptr)
- {
- // even if handler is nullptr, return typeof as "object"
- return requestContext->GetLibrary()->GetObjectTypeDisplayString();
- }
- // if exotic object has [[Call]] we should return "function", otherwise return "object"
- if (JavascriptFunction::Is(this->target))
- {
- return requestContext->GetLibrary()->GetFunctionTypeDisplayString();
- }
- else
- {
- return requestContext->GetLibrary()->GetObjectTypeDisplayString();
- }
- }
- BOOL JavascriptProxy::GetOwnPropertyDescriptor(RecyclableObject* obj, PropertyId propertyId, ScriptContext* requestContext, PropertyDescriptor* propertyDescriptor)
- {
- JavascriptProxy* proxy = JavascriptProxy::FromVar(obj);
- return proxy->GetPropertyDescriptorTrap(propertyId, propertyDescriptor, requestContext);
- }
- BOOL JavascriptProxy::DefineOwnPropertyDescriptor(RecyclableObject* obj, PropertyId propId, const PropertyDescriptor& descriptor, bool throwOnError, ScriptContext* requestContext)
- {
- PROBE_STACK(requestContext, Js::Constants::MinStackDefault);
- // Reject implicit call
- ThreadContext* threadContext = requestContext->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return FALSE;
- }
- JavascriptProxy* proxy = JavascriptProxy::FromVar(obj);
- //1. Assert: IsPropertyKey(P) is true.
- //2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
- RecyclableObject *handlerObj = proxy->MarshalHandler(requestContext);
- //3. If handler is null, then throw a TypeError exception.
- if (handlerObj == nullptr)
- {
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return FALSE;
- JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("definePropertyDescriptor"));
- }
- //4. Let target be the value of the[[ProxyTarget]] internal slot of O.
- RecyclableObject *targetObj = proxy->MarshalTarget(requestContext);
- //5. Let trap be the result of GetMethod(handler, "defineProperty").
- //6. ReturnIfAbrupt(trap).
- //7. If trap is undefined, then
- //a.Return the result of calling the[[DefineOwnProperty]] internal method of target with arguments P and Desc.
- JavascriptFunction* defineOwnPropertyMethod = proxy->GetMethodHelper(PropertyIds::defineProperty, requestContext);
- Assert(!requestContext->IsHeapEnumInProgress());
- if (nullptr == defineOwnPropertyMethod)
- {
- return JavascriptOperators::DefineOwnPropertyDescriptor(targetObj, propId, descriptor, throwOnError, requestContext);
- }
- //8. Let descObj be FromPropertyDescriptor(Desc).
- //9. NOTE If Desc was originally generated from an object using ToPropertyDescriptor, then descObj will be that original object.
- //10. Let trapResult be the result of calling the[[Call]] internal method of trap with handler as the this value and a new List containing target, P, and descObj.
- //11. Let booleanTrapResult be ToBoolean(trapResult).
- //12. ReturnIfAbrupt(booleanTrapResult).
- //13. If booleanTrapResult is false, then return false.
- //14. Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
- //15. ReturnIfAbrupt(targetDesc).
- Var descVar = descriptor.GetOriginal();
- if (descVar == nullptr)
- {
- descVar = JavascriptOperators::FromPropertyDescriptor(descriptor, requestContext);
- }
- Var propertyName = GetName(requestContext, propId);
- Var definePropertyResult = threadContext->ExecuteImplicitCall(defineOwnPropertyMethod, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, defineOwnPropertyMethod, CallInfo(CallFlags_Value, 4), handlerObj, targetObj, propertyName, descVar);
- });
- BOOL defineResult = JavascriptConversion::ToBoolean(definePropertyResult, requestContext);
- if (!defineResult)
- {
- return defineResult;
- }
- //16. Let extensibleTarget be the result of IsExtensible(target).
- //17. ReturnIfAbrupt(extensibleTarget).
- //18. If Desc has a[[Configurable]] field and if Desc.[[Configurable]] is false, then
- // a.Let settingConfigFalse be true.
- //19. Else let settingConfigFalse be false.
- //20. If targetDesc is undefined, then
- // a.If extensibleTarget is false, then throw a TypeError exception.
- // b.If settingConfigFalse is true, then throw a TypeError exception.
- //21. Else targetDesc is not undefined,
- // a.If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, then throw a TypeError exception.
- // b.If settingConfigFalse is true and targetDesc.[[Configurable]] is true, then throw a TypeError exception.
- //22. Return true.
- PropertyDescriptor targetDescriptor;
- BOOL hasProperty = JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propId, requestContext, &targetDescriptor);
- BOOL isExtensible = targetObj->IsExtensible();
- BOOL settingConfigFalse = (descriptor.ConfigurableSpecified() && !descriptor.IsConfigurable());
- if (!hasProperty)
- {
- if (!isExtensible || settingConfigFalse)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("defineProperty"));
- }
- }
- else
- {
- if (!JavascriptOperators::IsCompatiblePropertyDescriptor(descriptor, hasProperty? &targetDescriptor : nullptr, !!isExtensible, true, requestContext))
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("defineProperty"));
- }
- if (settingConfigFalse && targetDescriptor.IsConfigurable())
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("defineProperty"));
- }
- }
- return TRUE;
- }
- BOOL JavascriptProxy::SetPropertyTrap(Var receiver, SetPropertyTrapKind setPropertyTrapKind, Js::JavascriptString * propertyNameString, Var newValue, ScriptContext* requestContext)
- {
- const PropertyRecord* propertyRecord;
- requestContext->GetOrAddPropertyRecord(propertyNameString, &propertyRecord);
- return SetPropertyTrap(receiver, setPropertyTrapKind, propertyRecord->GetPropertyId(), newValue, requestContext);
- }
- BOOL JavascriptProxy::SetPropertyTrap(Var receiver, SetPropertyTrapKind setPropertyTrapKind, PropertyId propertyId, Var newValue, ScriptContext* requestContext, BOOL skipPrototypeCheck)
- {
- PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
- // Reject implicit call
- ThreadContext* threadContext = requestContext->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return FALSE;
- }
- //1. Assert: IsPropertyKey(P) is true.
- //2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
- Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
- //3. If handler is null, then throw a TypeError exception.
- if (handlerObj == nullptr)
- {
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return FALSE;
- JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("set"));
- }
- //4. Let target be the value of the[[ProxyTarget]] internal slot of O.
- Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
- //5. Let trap be the result of GetMethod(handler, "set").
- //6. ReturnIfAbrupt(trap).
- //7. If trap is undefined, then
- //a.Return the result of calling the[[Set]] internal method of target with arguments P, V, and Receiver.
- JavascriptFunction* setMethod = GetMethodHelper(PropertyIds::set, requestContext);
- Assert(!GetScriptContext()->IsHeapEnumInProgress());
- if (nullptr == setMethod)
- {
- PropertyValueInfo info;
- switch (setPropertyTrapKind)
- {
- case SetPropertyTrapKind::SetItemOnTaggedNumberKind:
- {
- uint32 indexVal;
- BOOL isNumericPropertyId = requestContext->IsNumericPropertyId(propertyId, &indexVal);
- Assert(isNumericPropertyId);
- return JavascriptOperators::SetItemOnTaggedNumber(receiver, targetObj, indexVal, newValue, requestContext, PropertyOperationFlags::PropertyOperation_None);
- }
- case SetPropertyTrapKind::SetPropertyOnTaggedNumberKind:
- return JavascriptOperators::SetPropertyOnTaggedNumber(receiver, targetObj, propertyId, newValue, requestContext, PropertyOperation_None);
- case SetPropertyTrapKind::SetPropertyKind:
- return JavascriptOperators::SetProperty(receiver, targetObj, propertyId, newValue, requestContext);
- case SetPropertyTrapKind::SetItemKind:
- {
- uint32 indexVal;
- BOOL isNumericPropertyId = requestContext->IsNumericPropertyId(propertyId, &indexVal);
- Assert(isNumericPropertyId);
- return JavascriptOperators::SetItem(receiver, targetObj, indexVal, newValue, requestContext, PropertyOperationFlags::PropertyOperation_None, skipPrototypeCheck);
- }
- case SetPropertyTrapKind::SetPropertyWPCacheKind:
- {
- PropertyValueInfo propertyValueInfo;
- return JavascriptOperators::SetPropertyWPCache(receiver, targetObj, propertyId, newValue, requestContext, PropertyOperationFlags::PropertyOperation_None, &propertyValueInfo);
- }
- default:
- AnalysisAssert(FALSE);
- }
- }
- //8. Let trapResult be the result of calling the[[Call]] internal method of trap with handler as the this value and a new List containing target, P, V, and Receiver.
- //9. Let booleanTrapResult be ToBoolean(trapResult).
- //10. ReturnIfAbrupt(booleanTrapResult).
- //11. If booleanTrapResult is false, then return false.
- Var propertyName = GetName(requestContext, propertyId);
-
- Var setPropertyResult = threadContext->ExecuteImplicitCall(setMethod, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, setMethod, CallInfo(CallFlags_Value, 5), handlerObj, targetObj, propertyName, newValue, receiver);
- });
-
- BOOL setResult = JavascriptConversion::ToBoolean(setPropertyResult, requestContext);
- if (!setResult)
- {
- return setResult;
- }
- //12. Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
- //13. ReturnIfAbrupt(targetDesc).
- //14. If targetDesc is not undefined, then
- //a.If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is false, then
- //i.If SameValue(V, targetDesc.[[Value]]) is false, then throw a TypeError exception.
- //b.If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false, then
- //i.If targetDesc.[[Set]] is undefined, then throw a TypeError exception.
- //15. Return true
- PropertyDescriptor targetDescriptor;
- BOOL hasProperty;
- hasProperty = JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetDescriptor);
- if (hasProperty)
- {
- if (targetDescriptor.ValueSpecified())
- {
- if (!targetDescriptor.IsConfigurable() && !targetDescriptor.IsWritable() &&
- !JavascriptConversion::SameValue(newValue, targetDescriptor.GetValue()))
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("set"));
- }
- }
- else
- {
- if (!targetDescriptor.IsConfigurable() && targetDescriptor.GetSetter() == requestContext->GetLibrary()->GetDefaultAccessorFunction())
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("set"));
- }
- }
- }
- return TRUE;
- }
- JavascriptFunction* JavascriptProxy::GetMethodHelper(PropertyId methodId, ScriptContext* requestContext)
- {
- //2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
- //3. If handler is null, then throw a TypeError exception.
- if (this->handler == nullptr)
- {
- // the proxy has been revoked; TypeError.
- JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, requestContext->GetPropertyName(methodId)->GetBuffer());
- }
- Var varMethod;
- //5. Let trap be the result of GetMethod(handler, "getOwnPropertyDescriptor").
- //6. ReturnIfAbrupt(trap).
- //7.3.9 GetMethod(V, P)
- // The abstract operation GetMethod is used to get the value of a specific property of an ECMAScript language value when the value of the
- // property is expected to be a function. The operation is called with arguments V and P where V is the ECMAScript language value, P is the
- // property key. This abstract operation performs the following steps:
- // 1. Assert: IsPropertyKey(P) is true.
- // 2. Let func be ? GetV(V, P).
- // 3. If func is either undefined or null, return undefined.
- // 4. If IsCallable(func) is false, throw a TypeError exception.
- // 5. Return func.
- BOOL result = JavascriptOperators::GetPropertyReference(handler, methodId, &varMethod, requestContext);
- if (!result || JavascriptOperators::IsUndefinedOrNull(varMethod))
- {
- return nullptr;
- }
- if (!JavascriptFunction::Is(varMethod))
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_NeedFunction, requestContext->GetPropertyName(methodId)->GetBuffer());
- }
- JavascriptFunction* function = JavascriptFunction::FromVar(varMethod);
- return JavascriptFunction::FromVar(CrossSite::MarshalVar(requestContext,
- function, function->GetScriptContext()));
- }
- Var JavascriptProxy::GetValueFromDescriptor(Var instance, PropertyDescriptor propertyDescriptor, ScriptContext* requestContext)
- {
- if (propertyDescriptor.ValueSpecified())
- {
- return CrossSite::MarshalVar(requestContext, propertyDescriptor.GetValue());
- }
- if (propertyDescriptor.GetterSpecified())
- {
- return JavascriptOperators::CallGetter(RecyclableObject::FromVar(propertyDescriptor.GetGetter()), instance, requestContext);
- }
- Assert(FALSE);
- return requestContext->GetLibrary()->GetUndefined();
- }
- void JavascriptProxy::PropertyIdFromInt(uint32 index, PropertyRecord const** propertyRecord)
- {
- char16 buffer[22];
- int pos = TaggedInt::ToBuffer(index, buffer, _countof(buffer));
- GetScriptContext()->GetOrAddPropertyRecord((LPCWSTR)buffer + pos, (_countof(buffer) - 1) - pos, propertyRecord);
- }
- Var JavascriptProxy::GetName(ScriptContext* requestContext, PropertyId propertyId)
- {
- const PropertyRecord* propertyRecord = requestContext->GetThreadContext()->GetPropertyName(propertyId);
- Var name;
- if (propertyRecord->IsSymbol())
- {
- name = requestContext->GetSymbol(propertyRecord);
- }
- else
- {
- name = requestContext->GetPropertyString(propertyRecord);
- }
- return name;
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- PropertyId JavascriptProxy::EnsureHandlerPropertyId(ScriptContext* scriptContext)
- {
- ThreadContext* threadContext = scriptContext->GetThreadContext();
- if (threadContext->handlerPropertyId == Js::Constants::NoProperty)
- {
- LPCWSTR autoProxyName;
- if (threadContext->GetAutoProxyName() != nullptr)
- {
- autoProxyName = threadContext->GetAutoProxyName();
- }
- else
- {
- autoProxyName = Js::Configuration::Global.flags.autoProxy;
- }
- threadContext->handlerPropertyId = threadContext->GetOrAddPropertyRecordBind(
- JsUtil::CharacterBuffer<WCHAR>(autoProxyName, static_cast<charcount_t>(wcslen(autoProxyName))))->GetPropertyId();
- }
- return threadContext->handlerPropertyId;
- }
- RecyclableObject* JavascriptProxy::AutoProxyWrapper(Var obj)
- {
- RecyclableObject* object = RecyclableObject::FromVar(obj);
- if (!JavascriptOperators::IsObject(object) || JavascriptProxy::Is(object))
- {
- return object;
- }
- ScriptContext* scriptContext = object->GetScriptContext();
- if (!scriptContext->GetThreadContext()->IsScriptActive())
- {
- return object;
- }
- if (!scriptContext->GetConfig()->IsES6ProxyEnabled())
- {
- return object;
- }
- Assert(Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag));
- PropertyId handlerId = EnsureHandlerPropertyId(scriptContext);
- GlobalObject* globalObject = scriptContext->GetLibrary()->GetGlobalObject();
- Var handler = nullptr;
- if (!JavascriptOperators::GetProperty(globalObject, handlerId, &handler, scriptContext))
- {
- handler = scriptContext->GetLibrary()->CreateObject();
- JavascriptOperators::SetProperty(globalObject, globalObject, handlerId, handler, scriptContext);
- }
- CallInfo callInfo(CallFlags_Value, 3);
- Var varArgs[3];
- Js::Arguments arguments(callInfo, varArgs);
- varArgs[0] = scriptContext->GetLibrary()->GetProxyConstructor();
- varArgs[1] = object;
- varArgs[2] = handler;
- return Create(scriptContext, arguments);
- }
- #endif
- Var JavascriptProxy::ConstructorTrap(Arguments args, ScriptContext* scriptContext, const Js::AuxArray<uint32> *spreadIndices)
- {
- PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
- Var functionResult;
- if (spreadIndices != nullptr)
- {
- functionResult = JavascriptFunction::CallSpreadFunction(this, args, spreadIndices);
- }
- else
- {
- functionResult = JavascriptFunction::CallFunction<true>(this, this->GetEntryPoint(), args);
- }
- return functionResult;
- }
- Var JavascriptProxy::FunctionCallTrap(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- BOOL hasOverridingNewTarget = args.HasNewTarget();
- bool isCtorSuperCall = JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
- bool isNewCall = args.IsNewCall() || hasOverridingNewTarget;
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- if (!JavascriptProxy::Is(function))
- {
- if (args.Info.Flags & CallFlags_New)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction, _u("construct"));
- }
- else
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction, _u("call"));
- }
- }
- Var newTarget = nullptr;
- JavascriptProxy* proxy = JavascriptProxy::FromVar(function);
- Js::RecyclableObject *handlerObj = proxy->handler;
- Js::RecyclableObject *targetObj = proxy->target;
- JavascriptFunction* callMethod;
- Assert(!scriptContext->IsHeapEnumInProgress());
- // To conform with ES6 spec 7.3.13
- if (hasOverridingNewTarget)
- {
- newTarget = args.Values[callInfo.Count];
- }
- else
- {
- newTarget = proxy;
- }
- if (args.Info.Flags & CallFlags_New)
- {
- callMethod = proxy->GetMethodHelper(PropertyIds::construct, scriptContext);
- }
- else
- {
- callMethod = proxy->GetMethodHelper(PropertyIds::apply, scriptContext);
- }
- if (!JavascriptConversion::IsCallable(targetObj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction, _u("call"));
- }
- if (nullptr == callMethod)
- {
- // newCount is ushort. If args count is greater than or equal to 65535, an integer
- // too many arguments
- if (args.Info.Count >= USHORT_MAX) //check against CallInfo::kMaxCountArgs if newCount is ever made int
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgListTooLarge);
- }
- // in [[construct]] case, we don't need to check if the function is a constructor: the function should throw there.
- Var newThisObject = nullptr;
- if (args.Info.Flags & CallFlags_New)
- {
- if (!JavascriptOperators::IsConstructor(targetObj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedFunction, _u("construct"));
- }
- // args.Values[0] will be null in the case where NewTarget is initially provided by proxy.
- if (!isCtorSuperCall || !args.Values[0])
- {
- newThisObject = JavascriptOperators::NewScObjectNoCtor(targetObj, scriptContext);
- args.Values[0] = newThisObject;
- }
- else
- {
- newThisObject = args.Values[0];
- }
- }
- ushort newCount = (ushort)args.Info.Count;
- if (isNewCall)
- {
- newCount++;
- if (!newCount)
- {
- ::Math::DefaultOverflowPolicy();
- }
- }
- AnalysisAssert(newCount >= (ushort)args.Info.Count);
- Var* newValues;
- 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;
- }
- CallInfo calleeInfo((CallFlags)(args.Info.Flags), args.Info.Count);
- if (isNewCall)
- {
- calleeInfo.Flags = (CallFlags)(calleeInfo.Flags | CallFlags_ExtraArg | CallFlags_NewTarget);
- }
- for (ushort argCount = 0; argCount < (ushort)args.Info.Count; argCount++)
- {
- AnalysisAssert(newCount >= ((ushort)args.Info.Count));
- AnalysisAssert(argCount < newCount);
- AnalysisAssert(argCount < (ushort)args.Info.Count);
- AnalysisAssert(sizeof(Var*) == sizeof(void*));
- AnalysisAssert(sizeof(Var*) * argCount < sizeof(void*) * newCount);
- #pragma prefast(suppress:__WARNING_WRITE_OVERRUN, "This is a false positive, and all of the above analysis asserts still didn't convince prefast of that.")
- newValues[argCount] = args.Values[argCount];
- }
- if (isNewCall)
- {
- AnalysisAssert(newCount == ((ushort)args.Info.Count) + 1);
- newValues[args.Info.Count] = newTarget;
- }
- Js::Arguments arguments(calleeInfo, newValues);
- Var aReturnValue = nullptr;
- BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
- {
- aReturnValue = JavascriptFunction::CallFunction<true>(targetObj, targetObj->GetEntryPoint(), arguments);
- }
- END_SAFE_REENTRANT_CALL
- // If this is constructor call, return the actual object instead of function result
- if ((callInfo.Flags & CallFlags_New) && !JavascriptOperators::IsObject(aReturnValue))
- {
- aReturnValue = newThisObject;
- }
- return aReturnValue;
- }
- JavascriptArray* argList = scriptContext->GetLibrary()->CreateArray(callInfo.Count - 1);
- for (uint i = 1; i < callInfo.Count; i++)
- {
- argList->DirectSetItemAt(i - 1, args[i]);
- }
- Var varArgs[4];
- CallInfo calleeInfo(CallFlags_Value, 4);
- Js::Arguments arguments(calleeInfo, varArgs);
- varArgs[0] = handlerObj;
- varArgs[1] = targetObj;
- if (args.Info.Flags & CallFlags_New)
- {
- if (!JavascriptOperators::IsConstructor(targetObj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NotAConstructor);
- }
- varArgs[2] = argList;
- // 1st preference - overridden newTarget
- // 2nd preference - 'this' in case of super() call
- // 3rd preference - newTarget ( which is same as F)
- varArgs[3] = hasOverridingNewTarget ? newTarget :
- isCtorSuperCall ? args[0] : newTarget;
- }
- else
- {
- varArgs[2] = args[0];
- varArgs[3] = argList;
- }
- Var trapResult = nullptr;
- BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
- {
- trapResult = callMethod->CallFunction(arguments);
- }
- END_SAFE_REENTRANT_CALL
- if (args.Info.Flags & CallFlags_New)
- {
- if (!Js::JavascriptOperators::IsObject(trapResult))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InconsistentTrapResult, _u("construct"));
- }
- }
- return trapResult;
- }
- JavascriptArray* JavascriptProxy::PropertyKeysTrap(KeysTrapKind keysTrapKind, ScriptContext* requestContext)
- {
- PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
- // Reject implicit call
- ThreadContext* threadContext = requestContext->GetThreadContext();
- if (threadContext->IsDisableImplicitCall())
- {
- threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
- return nullptr;
- }
- //1. Let handler be the value of the[[ProxyHandler]] internal slot of O.
- RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
- //2. If handler is null, throw a TypeError exception.
- //3. Assert: Type(handler) is Object.
- if (handlerObj == nullptr)
- {
- // the proxy has been revoked; TypeError.
- if (!threadContext->RecordImplicitException())
- return nullptr;
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("ownKeys"));
- }
- AssertMsg(JavascriptOperators::IsObject(handlerObj), "Handler should be object.");
- //4. Let target be the value of the[[ProxyTarget]] internal slot of O.
- RecyclableObject *targetObj = this->MarshalTarget(requestContext);
- //5. Let trap be GetMethod(handler, "ownKeys").
- //6. ReturnIfAbrupt(trap).
- //7. If trap is undefined, then
- // a. Return target.[[OwnPropertyKeys]]().
- JavascriptFunction* ownKeysMethod = GetMethodHelper(PropertyIds::ownKeys, requestContext);
- Assert(!GetScriptContext()->IsHeapEnumInProgress());
- JavascriptArray *targetKeys;
- if (nullptr == ownKeysMethod)
- {
- switch (keysTrapKind)
- {
- case GetOwnPropertyNamesKind:
- targetKeys = JavascriptOperators::GetOwnPropertyNames(targetObj, requestContext);
- break;
- case GetOwnPropertySymbolKind:
- targetKeys = JavascriptOperators::GetOwnPropertySymbols(targetObj, requestContext);
- break;
- case KeysKind:
- targetKeys = JavascriptOperators::GetOwnPropertyKeys(targetObj, requestContext);
- break;
- default:
- AssertMsg(false, "Invalid KeysTrapKind.");
- return requestContext->GetLibrary()->CreateArray(0);
- }
- return targetKeys;
- }
- //8. Let trapResultArray be Call(trap, handler, <<target>>).
- //9. Let trapResult be CreateListFromArrayLike(trapResultArray, <<String, Symbol>>).
- //10. ReturnIfAbrupt(trapResult).
- //11. Let extensibleTarget be IsExtensible(target).
- //12. ReturnIfAbrupt(extensibleTarget).
- //13. Let targetKeys be target.[[OwnPropertyKeys]]().
- //14. ReturnIfAbrupt(targetKeys).
-
- Var ownKeysResult = threadContext->ExecuteImplicitCall(ownKeysMethod, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, ownKeysMethod, CallInfo(CallFlags_Value, 2), handlerObj, targetObj);
- });
- if (!JavascriptOperators::IsObject(ownKeysResult))
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("ownKeys"));
- }
- RecyclableObject* trapResultArray = RecyclableObject::FromVar(ownKeysResult);
- BOOL isTargetExtensible = targetObj->IsExtensible();
- targetKeys = JavascriptOperators::GetOwnPropertyKeys(targetObj, requestContext);
- //15. Assert: targetKeys is a List containing only String and Symbol values.
- //16. Let targetConfigurableKeys be an empty List.
- //17. Let targetNonconfigurableKeys be an empty List.
- //18. Repeat, for each element key of targetKeys,
- // a.Let desc be target.[[GetOwnProperty]](key).
- // b.ReturnIfAbrupt(desc).
- // c.If desc is not undefined and desc.[[Configurable]] is false, then
- // i.Append key as an element of targetNonconfigurableKeys.
- // d.Else,
- // i.Append key as an element of targetConfigurableKeys.
- //19. If extensibleTarget is true and targetNonconfigurableKeys is empty, then
- // a. Return trapResult.
- //20. Let uncheckedResultKeys be a new List which is a copy of trapResult.
- //21. Repeat, for each key that is an element of targetNonconfigurableKeys,
- // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception.
- // b. Remove key from uncheckedResultKeys
- //22. If extensibleTarget is true, return trapResult.
- /*
- To avoid creating targetConfigurableKeys, targetNonconfigurableKeys and uncheckedResultKeys list in above steps,
- use below algorithm to accomplish same behavior
- // Track if there are any properties that are present in target but not present in trap result
- for(var i = 0; i < trapResult.length; i++)
- {
- PropertyId propId = GetPropertyId(trapResult[i]);
- if(propId != NoProperty) { targetToTrapResultMap[propId] = 1; }
- else { isTrapResultMissingFromTargetKeys = true; }
- }
- isConfigurableKeyMissingFromTrapResult = false;
- isNonconfigurableKeyMissingFromTrapResult = false;
- for(var i = 0; i < targetKeys.length; i++)
- {
- PropertyId propId = GetPropertyId(targetKeys[i]);
- Var desc = GetPropertyDescriptor(propId);
- if(targetToTrapResultMap[propId]) {
- delete targetToTrapResultMap[propId];
- isMissingFromTrapResult = false;
- } else {
- isMissingFromTrapResult = true;
- }
- if(desc->IsConfigurable()) {
- if(isMissingFromTrapResult) {
- isConfigurableKeyMissingFromTrapResult = true;
- }
- } else {
- isAnyNonconfigurableKeyPresent = true
- if(isMissingFromTrapResult) {
- isNonconfigurableKeyMissingFromTrapResult = true;
- }
- }
- }
- // 19.
- if(isExtensible && !isAnyNonconfigurableKeyPresent) { return trapResult; }
- // 21.
- if(isNonconfigurableKeyMissingFromTrapResult) { throw TypeError; }
- // 22.
- if(isExtensible) { return trapResult; }
- // 23.
- if(isConfigurableKeyMissingFromTrapResult) { throw TypeError; }
- // 24.
- if(!targetToTrapResultMap.Empty()) { throw TypeError; }
- return trapResult;
- */
- JavascriptArray* trapResult = requestContext->GetLibrary()->CreateArray(0);
- bool isConfigurableKeyMissingFromTrapResult = false;
- bool isNonconfigurableKeyMissingFromTrapResult = false;
- bool isKeyMissingFromTrapResult = false;
- bool isKeyMissingFromTargetResult = false;
- bool isAnyNonconfigurableKeyPresent = false;
- Var element;
- PropertyId propertyId;
- const PropertyRecord* propertyRecord = nullptr;
- BEGIN_TEMP_ALLOCATOR(tempAllocator, requestContext, _u("Runtime"))
- {
- // Dictionary containing intersection of keys present in targetKeys and trapResult
- Var lenValue = JavascriptOperators::OP_GetLength(trapResultArray, requestContext);
- uint32 len = (uint32)JavascriptConversion::ToLength(lenValue, requestContext);
- JsUtil::BaseDictionary<Js::PropertyId, bool, ArenaAllocator> targetToTrapResultMap(tempAllocator, len);
- // Trap result to return.
- // Note : This will not necessarily have all elements present in trapResultArray. E.g. If trap was called from GetOwnPropertySymbols()
- // trapResult will only contain symbol elements from trapResultArray.
- switch (keysTrapKind)
- {
- case GetOwnPropertyNamesKind:
- GetOwnPropertyKeysHelper(requestContext, trapResultArray, len, trapResult, targetToTrapResultMap,
- [&](const PropertyRecord *propertyRecord)->bool
- {
- return !propertyRecord->IsSymbol();
- });
- break;
- case GetOwnPropertySymbolKind:
- GetOwnPropertyKeysHelper(requestContext, trapResultArray, len, trapResult, targetToTrapResultMap,
- [&](const PropertyRecord *propertyRecord)->bool
- {
- return propertyRecord->IsSymbol();
- });
- break;
- case KeysKind:
- GetOwnPropertyKeysHelper(requestContext, trapResultArray, len, trapResult, targetToTrapResultMap,
- [&](const PropertyRecord *propertyRecord)->bool
- {
- return true;
- });
- break;
- }
- for (uint32 i = 0; i < targetKeys->GetLength(); i++)
- {
- element = targetKeys->DirectGetItem(i);
- AssertMsg(JavascriptSymbol::Is(element) || JavascriptString::Is(element), "Invariant check during ownKeys proxy trap should make sure we only get property key here. (symbol or string primitives)");
- JavascriptConversion::ToPropertyKey(element, requestContext, &propertyRecord, nullptr);
- propertyId = propertyRecord->GetPropertyId();
- if (propertyId == Constants::NoProperty)
- continue;
- // If not present in intersection means either the property is not present in targetKeys or
- // we have already visited the property in targetKeys
- if (targetToTrapResultMap.ContainsKey(propertyId))
- {
- isKeyMissingFromTrapResult = false;
- targetToTrapResultMap.Remove(propertyId);
- }
- else
- {
- isKeyMissingFromTrapResult = true;
- }
- PropertyDescriptor targetKeyPropertyDescriptor;
- if (Js::JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetKeyPropertyDescriptor) && !targetKeyPropertyDescriptor.IsConfigurable())
- {
- isAnyNonconfigurableKeyPresent = true;
- if (isKeyMissingFromTrapResult)
- {
- isNonconfigurableKeyMissingFromTrapResult = true;
- }
- }
- else
- {
- if (isKeyMissingFromTrapResult)
- {
- isConfigurableKeyMissingFromTrapResult = true;
- }
- }
- }
- // Keys that were not found in targetKeys will continue to remain in the map
- isKeyMissingFromTargetResult = targetToTrapResultMap.Count() != 0;
- }
- END_TEMP_ALLOCATOR(tempAllocator, requestContext)
- // 19.
- if (isTargetExtensible && !isAnyNonconfigurableKeyPresent)
- {
- return trapResult;
- }
- // 21.
- if (isNonconfigurableKeyMissingFromTrapResult)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("ownKeys"));
- }
- // 22.
- if (isTargetExtensible)
- {
- return trapResult;
- }
- // 23.
- if (isConfigurableKeyMissingFromTrapResult)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("ownKeys"));
- }
- // 24.
- if (isKeyMissingFromTargetResult)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("ownKeys"));
- }
- return trapResult;
- }
- #if ENABLE_TTD
- void JavascriptProxy::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
- {
- if(this->handler != nullptr)
- {
- extractor->MarkVisitVar(this->handler);
- }
- if(this->target != nullptr)
- {
- extractor->MarkVisitVar(this->target);
- }
- }
- TTD::NSSnapObjects::SnapObjectType JavascriptProxy::GetSnapTag_TTD() const
- {
- return TTD::NSSnapObjects::SnapObjectType::SnapProxyObject;
- }
- void JavascriptProxy::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
- {
- TTD::NSSnapObjects::SnapProxyInfo* spi = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapProxyInfo>();
- const uint32 reserveSize = 2;
- uint32 depOnCount = 0;
- TTD_PTR_ID* depOnArray = alloc.SlabReserveArraySpace<TTD_PTR_ID>(reserveSize);
- spi->HandlerId = TTD_INVALID_PTR_ID;
- if(this->handler != nullptr)
- {
- spi->HandlerId = TTD_CONVERT_VAR_TO_PTR_ID(this->handler);
- if(TTD::JsSupport::IsVarComplexKind(this->handler))
- {
- depOnArray[depOnCount] = TTD_CONVERT_VAR_TO_PTR_ID(this->handler);
- depOnCount++;
- }
- }
- spi->TargetId = TTD_INVALID_PTR_ID;
- if(this->target != nullptr)
- {
- spi->TargetId = TTD_CONVERT_VAR_TO_PTR_ID(this->target);
- if(TTD::JsSupport::IsVarComplexKind(this->handler))
- {
- depOnArray[depOnCount] = TTD_CONVERT_VAR_TO_PTR_ID(this->target);
- depOnCount++;
- }
- }
- if(depOnCount == 0)
- {
- alloc.SlabAbortArraySpace<TTD_PTR_ID>(reserveSize);
- TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapProxyInfo*, TTD::NSSnapObjects::SnapObjectType::SnapProxyObject>(objData, spi);
- }
- else
- {
- alloc.SlabCommitArraySpace<TTD_PTR_ID>(depOnCount, reserveSize);
- TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapProxyInfo*, TTD::NSSnapObjects::SnapObjectType::SnapProxyObject>(objData, spi, alloc, depOnCount, depOnArray);
- }
- }
- #endif
- }
|