| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571 |
- //-------------------------------------------------------------------------------------------------------
- // 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;
- }
- template <class Fn, class GetPropertyIdFunc>
- BOOL JavascriptProxy::GetPropertyDescriptorTrap(Var originalInstance, Fn fn, GetPropertyIdFunc getPropertyId, 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 fn(targetObj);
- }
- PropertyId propertyId = getPropertyId();
- 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;
- hasProperty = JavascriptOperators::GetOwnPropertyDescriptor(targetObj, getPropertyId(), 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"));
- }
- if (!target->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.
- BOOL isTargetExtensible = target->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)
- {
- 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.
- 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(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;
- }
-
- BOOL JavascriptProxy::GetAccessors(PropertyId propertyId, __out Var* getter, __out 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;
- }
- BOOL JavascriptProxy::GetDefaultPropertyDescriptor(PropertyDescriptor& descriptor)
- {
- if (target == nullptr)
- {
- JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u(""));
- }
- return target->GetDefaultPropertyDescriptor(descriptor);
- }
- // 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);
- auto fn = [&](RecyclableObject *targetObj)-> BOOL {
- return JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, propertyDescriptor);
- };
- auto getPropertyId = [&]() -> PropertyId {return propertyId; };
- BOOL foundProperty = proxy->GetPropertyDescriptorTrap(obj, fn, getPropertyId, propertyDescriptor, requestContext);
- return foundProperty;
- }
- 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"));
- }
- newThisObject = JavascriptOperators::NewScObjectNoCtor(targetObj, scriptContext);
- args.Values[0] = newThisObject;
- }
- ushort newCount = (ushort)args.Info.Count;
- if (isNewCall)
- {
- newCount++;
- if (!newCount)
- {
- ::Math::DefaultOverflowPolicy();
- }
- }
- 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 (uint argCount = 0; argCount < args.Info.Count; argCount++)
- {
- newValues[argCount] = args.Values[argCount];
- }
- #pragma prefast(suppress:6386)
- if (isNewCall)
- {
- newValues[args.Info.Count] = newTarget;
- }
- Js::Arguments arguments(calleeInfo, newValues);
- Var aReturnValue = JavascriptFunction::CallFunction<true>(targetObj, targetObj->GetEntryPoint(), arguments);
- // 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 = callMethod->CallFunction(arguments);
- 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
- }
|