JavascriptProxy.cpp 113 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLibraryPch.h"
  6. namespace Js
  7. {
  8. BOOL JavascriptProxy::Is(_In_ RecyclableObject* obj)
  9. {
  10. return JavascriptOperators::GetTypeId(obj) == TypeIds_Proxy;
  11. }
  12. BOOL JavascriptProxy::Is(_In_ Var obj)
  13. {
  14. return JavascriptOperators::GetTypeId(obj) == TypeIds_Proxy;
  15. }
  16. bool JavascriptProxy::IsRevoked() const
  17. {
  18. return (target == nullptr);
  19. }
  20. RecyclableObject* JavascriptProxy::GetTarget()
  21. {
  22. if (target == nullptr)
  23. {
  24. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u(""));
  25. }
  26. return target;
  27. }
  28. RecyclableObject* JavascriptProxy::GetHandler()
  29. {
  30. if (handler == nullptr)
  31. {
  32. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u(""));
  33. }
  34. return handler;
  35. }
  36. Var JavascriptProxy::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  37. {
  38. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  39. ARGUMENTS(args, callInfo);
  40. ScriptContext* scriptContext = function->GetScriptContext();
  41. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  42. CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(ES6, Proxy, scriptContext);
  43. if (!(args.Info.Flags & CallFlags_New))
  44. {
  45. JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Proxy"));
  46. }
  47. JavascriptProxy* proxy = JavascriptProxy::Create(scriptContext, args);
  48. return proxy;
  49. }
  50. JavascriptProxy* JavascriptProxy::Create(ScriptContext* scriptContext, Arguments args)
  51. {
  52. // SkipDefaultNewObject function flag should have prevented the default object from
  53. // being created, except when call true a host dispatch.
  54. Var newTarget = args.GetNewTarget();
  55. bool isCtorSuperCall = JavascriptOperators::IsConstructorSuperCall(args);
  56. RecyclableObject* target, *handler;
  57. if (args.Info.Count < 3)
  58. {
  59. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedProxyArgument);
  60. }
  61. if (!JavascriptOperators::IsObjectType(JavascriptOperators::GetTypeId(args[1])))
  62. {
  63. JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidProxyArgument, _u("target"));
  64. }
  65. target = DynamicObject::FromVar(args[1]);
  66. #if ENABLE_COPYONACCESS_ARRAY
  67. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(target);
  68. #endif
  69. if (JavascriptProxy::Is(target))
  70. {
  71. if (JavascriptProxy::FromVar(target)->target == nullptr)
  72. {
  73. JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidProxyArgument, _u("target"));
  74. }
  75. }
  76. if (!JavascriptOperators::IsObjectType(JavascriptOperators::GetTypeId(args[2])))
  77. {
  78. JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidProxyArgument, _u("handler"));
  79. }
  80. handler = DynamicObject::FromVar(args[2]);
  81. if (JavascriptProxy::Is(handler))
  82. {
  83. if (JavascriptProxy::FromVar(handler)->handler == nullptr)
  84. {
  85. JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidProxyArgument, _u("handler"));
  86. }
  87. }
  88. JavascriptProxy* newProxy = RecyclerNew(scriptContext->GetRecycler(), JavascriptProxy, scriptContext->GetLibrary()->GetProxyType(), scriptContext, target, handler);
  89. if (JavascriptConversion::IsCallable(target))
  90. {
  91. newProxy->ChangeType();
  92. newProxy->GetDynamicType()->SetEntryPoint(JavascriptProxy::FunctionCallTrap);
  93. }
  94. return isCtorSuperCall ?
  95. JavascriptProxy::FromVar(JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), newProxy, nullptr, scriptContext)) :
  96. newProxy;
  97. }
  98. Var JavascriptProxy::EntryRevocable(RecyclableObject* function, CallInfo callInfo, ...)
  99. {
  100. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  101. ARGUMENTS(args, callInfo);
  102. ScriptContext* scriptContext = function->GetScriptContext();
  103. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Proxy.revocable"));
  104. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  105. if (args.Info.Flags & CallFlags_New)
  106. {
  107. JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Proxy.revocable"));
  108. }
  109. JavascriptProxy* proxy = JavascriptProxy::Create(scriptContext, args);
  110. JavascriptLibrary* library = scriptContext->GetLibrary();
  111. DynamicType* type = library->CreateFunctionWithConfigurableLengthType(&EntryInfo::Revoke);
  112. RuntimeFunction* revoker = RecyclerNewEnumClass(scriptContext->GetRecycler(),
  113. JavascriptLibrary::EnumFunctionClass, RuntimeFunction,
  114. type, &EntryInfo::Revoke);
  115. revoker->SetPropertyWithAttributes(Js::PropertyIds::length, Js::TaggedInt::ToVarUnchecked(2), PropertyConfigurable, NULL);
  116. revoker->SetInternalProperty(Js::InternalPropertyIds::RevocableProxy, proxy, PropertyOperationFlags::PropertyOperation_Force, nullptr);
  117. DynamicObject* obj = scriptContext->GetLibrary()->CreateObject(true, 2);
  118. JavascriptOperators::SetProperty(obj, obj, PropertyIds::proxy, proxy, scriptContext);
  119. JavascriptOperators::SetProperty(obj, obj, PropertyIds::revoke, revoker, scriptContext);
  120. return obj;
  121. }
  122. Var JavascriptProxy::EntryRevoke(RecyclableObject* function, CallInfo callInfo, ...)
  123. {
  124. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  125. ARGUMENTS(args, callInfo);
  126. ScriptContext* scriptContext = function->GetScriptContext();
  127. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Proxy.revoke"));
  128. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  129. Var revokableProxy;
  130. if (!function->GetInternalProperty(function, Js::InternalPropertyIds::RevocableProxy, &revokableProxy, nullptr, scriptContext))
  131. {
  132. JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidProxyArgument, _u(""));
  133. }
  134. TypeId typeId = JavascriptOperators::GetTypeId(revokableProxy);
  135. if (typeId == TypeIds_Null)
  136. {
  137. return scriptContext->GetLibrary()->GetUndefined();
  138. }
  139. if (typeId != TypeIds_Proxy)
  140. {
  141. JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidProxyArgument, _u(""));
  142. }
  143. function->SetInternalProperty(Js::InternalPropertyIds::RevocableProxy, scriptContext->GetLibrary()->GetNull(), PropertyOperationFlags::PropertyOperation_Force, nullptr);
  144. (JavascriptProxy::FromVar(revokableProxy))->RevokeObject();
  145. return scriptContext->GetLibrary()->GetUndefined();
  146. }
  147. JavascriptProxy::JavascriptProxy(DynamicType * type) :
  148. DynamicObject(type),
  149. handler(nullptr),
  150. target(nullptr)
  151. {
  152. type->SetHasSpecialPrototype(true);
  153. }
  154. JavascriptProxy::JavascriptProxy(DynamicType * type, ScriptContext * scriptContext, RecyclableObject* target, RecyclableObject* handler) :
  155. DynamicObject(type),
  156. handler(handler),
  157. target(target)
  158. {
  159. type->SetHasSpecialPrototype(true);
  160. }
  161. void JavascriptProxy::RevokeObject()
  162. {
  163. handler = nullptr;
  164. target = nullptr;
  165. }
  166. BOOL JavascriptProxy::GetPropertyDescriptorTrap(PropertyId propertyId, PropertyDescriptor* resultDescriptor, ScriptContext* requestContext)
  167. {
  168. PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
  169. // Reject implicit call
  170. ThreadContext* threadContext = requestContext->GetThreadContext();
  171. if (threadContext->IsDisableImplicitCall())
  172. {
  173. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  174. return FALSE;
  175. }
  176. //1. Let handler be the value of the[[ProxyHandler]] internal slot of O.
  177. Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
  178. //2. If handler is null, then throw a TypeError exception.
  179. if (handlerObj == nullptr)
  180. {
  181. if (!threadContext->RecordImplicitException())
  182. return FALSE;
  183. JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("getOwnPropertyDescriptor"));
  184. }
  185. //3. Let target be the value of the[[ProxyTarget]] internal slot of O.
  186. Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
  187. Assert((static_cast<DynamicType*>(GetType()))->GetTypeHandler()->GetPropertyCount() == 0 ||
  188. (static_cast<DynamicType*>(GetType()))->GetTypeHandler()->GetPropertyId(GetScriptContext(), 0) == InternalPropertyIds::WeakMapKeyMap);
  189. JavascriptFunction* gOPDMethod = GetMethodHelper(PropertyIds::getOwnPropertyDescriptor, requestContext);
  190. //7. If trap is undefined, then
  191. // a.Return the result of calling the[[GetOwnProperty]] internal method of target with argument P.
  192. if (nullptr == gOPDMethod || GetScriptContext()->IsHeapEnumInProgress())
  193. {
  194. resultDescriptor->SetFromProxy(false);
  195. return JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, resultDescriptor);
  196. }
  197. Var propertyName = GetName(requestContext, propertyId);
  198. Assert(JavascriptString::Is(propertyName) || JavascriptSymbol::Is(propertyName));
  199. //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.
  200. //9. ReturnIfAbrupt(trapResultObj).
  201. //10. If Type(trapResultObj) is neither Object nor Undefined, then throw a TypeError exception.
  202. Var getResult = threadContext->ExecuteImplicitCall(gOPDMethod, ImplicitCall_Accessor, [=]()->Js::Var
  203. {
  204. return CALL_FUNCTION(threadContext, gOPDMethod, CallInfo(CallFlags_Value, 3), handlerObj, targetObj, propertyName);
  205. });
  206. TypeId getResultTypeId = JavascriptOperators::GetTypeId(getResult);
  207. if (StaticType::Is(getResultTypeId) && getResultTypeId != TypeIds_Undefined)
  208. {
  209. JavascriptError::ThrowTypeError(requestContext, JSERR_NeedObject, _u("getOwnPropertyDescriptor"));
  210. }
  211. //11. Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
  212. //12. ReturnIfAbrupt(targetDesc).
  213. PropertyDescriptor targetDescriptor;
  214. BOOL hasProperty = JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetDescriptor);
  215. //13. If trapResultObj is undefined, then
  216. //a.If targetDesc is undefined, then return undefined.
  217. //b.If targetDesc.[[Configurable]] is false, then throw a TypeError exception.
  218. //c.Let extensibleTarget be the result of IsExtensible(target).
  219. //d.ReturnIfAbrupt(extensibleTarget).
  220. //e.If ToBoolean(extensibleTarget) is false, then throw a TypeError exception.
  221. //f.Return undefined.
  222. if (getResultTypeId == TypeIds_Undefined)
  223. {
  224. if (!hasProperty)
  225. {
  226. return FALSE;
  227. }
  228. if (!targetDescriptor.IsConfigurable())
  229. {
  230. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getOwnPropertyDescriptor"));
  231. }
  232. // do not use "target" here, the trap may have caused it to change
  233. if (!targetObj->IsExtensible())
  234. {
  235. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getOwnPropertyDescriptor"));
  236. }
  237. return FALSE;
  238. }
  239. //14. Let extensibleTarget be the result of IsExtensible(target).
  240. //15. ReturnIfAbrupt(extensibleTarget).
  241. //16. Let resultDesc be ToPropertyDescriptor(trapResultObj).
  242. //17. ReturnIfAbrupt(resultDesc).
  243. //18. Call CompletePropertyDescriptor(resultDesc, targetDesc).
  244. //19. Let valid be the result of IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc).
  245. //20. If valid is false, then throw a TypeError exception.
  246. //21. If resultDesc.[[Configurable]] is false, then
  247. //a.If targetDesc is undefined or targetDesc.[[Configurable]] is true, then
  248. //i.Throw a TypeError exception.
  249. //22. Return resultDesc.
  250. // do not use "target" here, the trap may have caused it to change
  251. BOOL isTargetExtensible = targetObj->IsExtensible();
  252. BOOL toProperty = JavascriptOperators::ToPropertyDescriptor(getResult, resultDescriptor, requestContext);
  253. if (!toProperty && isTargetExtensible)
  254. {
  255. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getOwnPropertyDescriptor"));
  256. }
  257. JavascriptOperators::CompletePropertyDescriptor(resultDescriptor, nullptr, requestContext);
  258. if (!JavascriptOperators::IsCompatiblePropertyDescriptor(*resultDescriptor, hasProperty ? &targetDescriptor : nullptr, !!isTargetExtensible, true, requestContext))
  259. {
  260. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getOwnPropertyDescriptor"));
  261. }
  262. if (!resultDescriptor->IsConfigurable())
  263. {
  264. if (!hasProperty || targetDescriptor.IsConfigurable())
  265. {
  266. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getOwnPropertyDescriptor"));
  267. }
  268. }
  269. resultDescriptor->SetFromProxy(true);
  270. return toProperty;
  271. }
  272. template <class Fn, class GetPropertyIdFunc>
  273. BOOL JavascriptProxy::GetPropertyTrap(Var instance, PropertyDescriptor* propertyDescriptor, Fn fn, GetPropertyIdFunc getPropertyId, ScriptContext* requestContext)
  274. {
  275. PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
  276. // Reject implicit call
  277. ThreadContext* threadContext = requestContext->GetThreadContext();
  278. if (threadContext->IsDisableImplicitCall())
  279. {
  280. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  281. return FALSE;
  282. }
  283. Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
  284. if (handlerObj == nullptr)
  285. {
  286. // the proxy has been revoked; TypeError.
  287. if (!threadContext->RecordImplicitException())
  288. return FALSE;
  289. JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("get"));
  290. }
  291. RecyclableObject *targetObj = this->MarshalTarget(requestContext);
  292. JavascriptFunction* getGetMethod = GetMethodHelper(PropertyIds::get, requestContext);
  293. if (nullptr == getGetMethod || requestContext->IsHeapEnumInProgress())
  294. {
  295. propertyDescriptor->SetFromProxy(false);
  296. return fn(targetObj);
  297. }
  298. PropertyId propertyId = getPropertyId();
  299. propertyDescriptor->SetFromProxy(true);
  300. Var propertyName = GetName(requestContext, propertyId);
  301. Var getGetResult = threadContext->ExecuteImplicitCall(getGetMethod, ImplicitCall_Accessor, [=]()->Js::Var
  302. {
  303. return CALL_FUNCTION(threadContext, getGetMethod, CallInfo(CallFlags_Value, 4), handlerObj, targetObj, propertyName, instance);
  304. });
  305. // 9. Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
  306. // 10. ReturnIfAbrupt(targetDesc).
  307. // 11. If targetDesc is not undefined, then
  308. // a.If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is false, then
  309. // i.If SameValue(trapResult, targetDesc.[[Value]]) is false, then throw a TypeError exception.
  310. // b.If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Get]] is undefined, then
  311. // i.If trapResult is not undefined, then throw a TypeError exception.
  312. // 12. Return trapResult.
  313. PropertyDescriptor targetDescriptor;
  314. Var defaultAccessor = requestContext->GetLibrary()->GetDefaultAccessorFunction();
  315. if (JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetDescriptor))
  316. {
  317. JavascriptOperators::CompletePropertyDescriptor(&targetDescriptor, nullptr, requestContext);
  318. if (targetDescriptor.ValueSpecified() && !targetDescriptor.IsConfigurable() && !targetDescriptor.IsWritable())
  319. {
  320. if (!JavascriptConversion::SameValue(getGetResult, targetDescriptor.GetValue()))
  321. {
  322. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("get"));
  323. }
  324. }
  325. else if (targetDescriptor.GetterSpecified() || targetDescriptor.SetterSpecified())
  326. {
  327. if (!targetDescriptor.IsConfigurable() &&
  328. targetDescriptor.GetGetter() == defaultAccessor &&
  329. JavascriptOperators::GetTypeId(getGetResult) != TypeIds_Undefined)
  330. {
  331. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("get"));
  332. }
  333. }
  334. }
  335. propertyDescriptor->SetValue(getGetResult);
  336. return TRUE;
  337. }
  338. template <class Fn, class GetPropertyIdFunc>
  339. BOOL JavascriptProxy::HasPropertyTrap(Fn fn, GetPropertyIdFunc getPropertyId)
  340. {
  341. PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
  342. // Reject implicit call
  343. ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
  344. if (threadContext->IsDisableImplicitCall())
  345. {
  346. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  347. return FALSE;
  348. }
  349. // Caller does not pass requestContext. Retrieve from host scriptContext stack.
  350. ScriptContext* requestContext =
  351. threadContext->GetPreviousHostScriptContext()->GetScriptContext();
  352. Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
  353. if (handlerObj == nullptr)
  354. {
  355. // the proxy has been revoked; TypeError.
  356. if (!threadContext->RecordImplicitException())
  357. return FALSE;
  358. JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("has"));
  359. }
  360. Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
  361. JavascriptFunction* hasMethod = GetMethodHelper(PropertyIds::has, requestContext);
  362. if (nullptr == hasMethod || requestContext->IsHeapEnumInProgress())
  363. {
  364. return fn(targetObj);
  365. }
  366. PropertyId propertyId = getPropertyId();
  367. Var propertyName = GetName(requestContext, propertyId);
  368. Var getHasResult = threadContext->ExecuteImplicitCall(hasMethod, ImplicitCall_Accessor, [=]()->Js::Var
  369. {
  370. return CALL_FUNCTION(threadContext, hasMethod, CallInfo(CallFlags_Value, 3), handlerObj, targetObj, propertyName);
  371. });
  372. //9. Let booleanTrapResult be ToBoolean(trapResult).
  373. //10. ReturnIfAbrupt(booleanTrapResult).
  374. //11. If booleanTrapResult is false, then
  375. // a.Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
  376. // b.ReturnIfAbrupt(targetDesc).
  377. // c.If targetDesc is not undefined, then
  378. // i.If targetDesc.[[Configurable]] is false, then throw a TypeError exception.
  379. // ii.Let extensibleTarget be the result of IsExtensible(target).
  380. // iii.ReturnIfAbrupt(extensibleTarget).
  381. // iv.If ToBoolean(extensibleTarget) is false, then throw a TypeError exception
  382. BOOL hasProperty = JavascriptConversion::ToBoolean(getHasResult, requestContext);
  383. if (!hasProperty)
  384. {
  385. PropertyDescriptor targetDescriptor;
  386. BOOL hasTargetProperty = JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetDescriptor);
  387. if (hasTargetProperty)
  388. {
  389. if (!targetDescriptor.IsConfigurable() || !targetObj->IsExtensible())
  390. {
  391. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("has"));
  392. }
  393. }
  394. }
  395. return hasProperty;
  396. }
  397. PropertyQueryFlags JavascriptProxy::HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info)
  398. {
  399. if (info)
  400. {
  401. // Prevent caching. See comment in GetPropertyQuery for more detail.
  402. PropertyValueInfo::SetNoCache(info, this);
  403. PropertyValueInfo::DisablePrototypeCache(info, this);
  404. }
  405. auto fn = [&](RecyclableObject* object)->BOOL {
  406. return JavascriptOperators::HasProperty(object, propertyId);
  407. };
  408. auto getPropertyId = [&]() ->PropertyId {
  409. return propertyId;
  410. };
  411. return JavascriptConversion::BooleanToPropertyQueryFlags(HasPropertyTrap(fn, getPropertyId));
  412. }
  413. BOOL JavascriptProxy::HasOwnProperty(PropertyId propertyId)
  414. {
  415. // should never come here and it will be redirected to GetOwnPropertyDescriptor
  416. Assert(FALSE);
  417. PropertyDescriptor propertyDesc;
  418. return GetOwnPropertyDescriptor(this, propertyId, GetScriptContext(), &propertyDesc);
  419. }
  420. BOOL JavascriptProxy::HasOwnPropertyNoHostObject(PropertyId propertyId)
  421. {
  422. // the virtual method is for checking if globalobject has local property before we start initializing
  423. // we shouldn't trap??
  424. Assert(FALSE);
  425. return HasProperty(propertyId);
  426. }
  427. BOOL JavascriptProxy::HasOwnPropertyCheckNoRedecl(PropertyId propertyId)
  428. {
  429. // root object and activation object verification only; not needed.
  430. Assert(FALSE);
  431. return false;
  432. }
  433. BOOL JavascriptProxy::UseDynamicObjectForNoHostObjectAccess()
  434. {
  435. // heapenum check for CEO etc., and we don't want to access external method during enumeration. not applicable here.
  436. Assert(FALSE);
  437. return false;
  438. }
  439. DescriptorFlags JavascriptProxy::GetSetter(PropertyId propertyId, Var* setterValueOrProxy, PropertyValueInfo* info, ScriptContext* requestContext)
  440. {
  441. // 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
  442. // code to mimic the 'one step prototype chain lookup' spec letter. Current code structure is enough.
  443. *setterValueOrProxy = this;
  444. PropertyValueInfo::SetNoCache(info, this);
  445. PropertyValueInfo::DisablePrototypeCache(info, this); // We can't cache prototype property either
  446. return DescriptorFlags::Proxy;
  447. }
  448. // GetSetter is called for
  449. DescriptorFlags JavascriptProxy::GetSetter(JavascriptString* propertyNameString, Var* setterValueOrProxy, PropertyValueInfo* info, ScriptContext* requestContext)
  450. {
  451. *setterValueOrProxy = this;
  452. PropertyValueInfo::SetNoCache(info, this);
  453. PropertyValueInfo::DisablePrototypeCache(info, this); // We can't cache prototype property either
  454. return DescriptorFlags::Proxy;
  455. }
  456. PropertyQueryFlags JavascriptProxy::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  457. {
  458. // 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
  459. // invalidate when target, handler, or handler prototype has changed. We don't have a way to achieve this yet.
  460. // Also, Get and Has operations share a cache, so a trap on either should prevent caching on both.
  461. PropertyValueInfo::SetNoCache(info, this);
  462. PropertyValueInfo::DisablePrototypeCache(info, this); // We can't cache prototype property either
  463. auto fn = [&](RecyclableObject* object)-> BOOL {
  464. return JavascriptOperators::GetProperty(originalInstance, object, propertyId, value, requestContext, nullptr);
  465. };
  466. auto getPropertyId = [&]()->PropertyId {return propertyId; };
  467. PropertyDescriptor result;
  468. BOOL foundProperty = GetPropertyTrap(originalInstance, &result, fn, getPropertyId, requestContext);
  469. if (!foundProperty)
  470. {
  471. *value = requestContext->GetMissingPropertyResult();
  472. }
  473. else if (result.IsFromProxy())
  474. {
  475. *value = GetValueFromDescriptor(originalInstance, result, requestContext);
  476. }
  477. return JavascriptConversion::BooleanToPropertyQueryFlags(foundProperty);
  478. }
  479. PropertyQueryFlags JavascriptProxy::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  480. {
  481. // 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
  482. // invalidate when target, handler, or handler prototype has changed. We don't have a way to achieve this yet.
  483. PropertyValueInfo::SetNoCache(info, this);
  484. PropertyValueInfo::DisablePrototypeCache(info, this); // We can't cache prototype property either
  485. auto fn = [&](RecyclableObject* object)-> BOOL {
  486. return JavascriptOperators::GetPropertyWPCache<false /* OutputExistence */>(originalInstance, object, propertyNameString, value, requestContext, info);
  487. };
  488. auto getPropertyId = [&]()->PropertyId{
  489. const PropertyRecord* propertyRecord;
  490. requestContext->GetOrAddPropertyRecord(propertyNameString, &propertyRecord);
  491. return propertyRecord->GetPropertyId();
  492. };
  493. PropertyDescriptor result;
  494. BOOL foundProperty = GetPropertyTrap(originalInstance, &result, fn, getPropertyId, requestContext);
  495. if (!foundProperty)
  496. {
  497. *value = requestContext->GetMissingPropertyResult();
  498. }
  499. else if (result.IsFromProxy())
  500. {
  501. *value = GetValueFromDescriptor(originalInstance, result, requestContext);
  502. }
  503. return JavascriptConversion::BooleanToPropertyQueryFlags(foundProperty);
  504. }
  505. BOOL JavascriptProxy::GetInternalProperty(Var instance, PropertyId internalPropertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  506. {
  507. if (internalPropertyId == InternalPropertyIds::WeakMapKeyMap)
  508. {
  509. return __super::GetInternalProperty(instance, internalPropertyId, value, info, requestContext);
  510. }
  511. return FALSE;
  512. }
  513. _Check_return_ _Success_(return) BOOL JavascriptProxy::GetAccessors(PropertyId propertyId, _Outptr_result_maybenull_ Var* getter, _Outptr_result_maybenull_ Var* setter, ScriptContext* requestContext)
  514. {
  515. PropertyDescriptor result;
  516. if (getter != nullptr)
  517. {
  518. *getter = nullptr;
  519. }
  520. if (setter != nullptr)
  521. {
  522. *setter = nullptr;
  523. }
  524. BOOL foundProperty = GetOwnPropertyDescriptor(this, propertyId, requestContext, &result);
  525. if (foundProperty && result.IsFromProxy())
  526. {
  527. if (result.GetterSpecified() && getter != nullptr)
  528. {
  529. *getter = result.GetGetter();
  530. }
  531. if (result.SetterSpecified() && setter != nullptr)
  532. {
  533. *setter = result.GetSetter();
  534. }
  535. foundProperty = result.GetterSpecified() || result.SetterSpecified();
  536. }
  537. return foundProperty;
  538. }
  539. PropertyQueryFlags JavascriptProxy::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  540. {
  541. // 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
  542. // invalidate when target, handler, or handler prototype has changed. We don't have a way to achieve this yet.
  543. PropertyValueInfo::SetNoCache(info, this);
  544. PropertyValueInfo::DisablePrototypeCache(info, this); // We can't cache prototype property either
  545. auto fn = [&](RecyclableObject* object)-> BOOL {
  546. return JavascriptOperators::GetPropertyReference(originalInstance, object, propertyId, value, requestContext, nullptr);
  547. };
  548. auto getPropertyId = [&]() -> PropertyId {return propertyId; };
  549. PropertyDescriptor result;
  550. BOOL foundProperty = GetPropertyTrap(originalInstance, &result, fn, getPropertyId, requestContext);
  551. if (!foundProperty)
  552. {
  553. *value = requestContext->GetMissingPropertyResult();
  554. }
  555. else if (result.IsFromProxy())
  556. {
  557. *value = GetValueFromDescriptor(originalInstance, result, requestContext);
  558. }
  559. return JavascriptConversion::BooleanToPropertyQueryFlags(foundProperty);
  560. }
  561. BOOL JavascriptProxy::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
  562. {
  563. PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
  564. // This is the second half of [[set]] where when the handler does not specified [[set]] so we forward to [[set]] on target
  565. // with receiver as the proxy.
  566. //c.Let existingDescriptor be the result of calling the[[GetOwnProperty]] internal method of Receiver with argument P.
  567. //d.ReturnIfAbrupt(existingDescriptor).
  568. //e.If existingDescriptor is not undefined, then
  569. // i.Let valueDesc be the PropertyDescriptor{ [[Value]]: V }.
  570. // ii.Return the result of calling the[[DefineOwnProperty]] internal method of Receiver with arguments P and valueDesc.
  571. //f.Else Receiver does not currently have a property P,
  572. // i.Return the result of performing CreateDataProperty(Receiver, P, V).
  573. // 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
  574. // invalidate when target, handler, or handler prototype has changed. We don't have a way to achieve this yet.
  575. PropertyValueInfo::SetNoCache(info, this);
  576. PropertyValueInfo::DisablePrototypeCache(info, this); // We can't cache prototype property either
  577. PropertyDescriptor proxyPropertyDescriptor;
  578. ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
  579. ScriptContext* requestContext =
  580. threadContext->GetPreviousHostScriptContext()->GetScriptContext();
  581. // Set implicit call flag so we bailout and not do copy-prop on field
  582. Js::ImplicitCallFlags saveImplicitCallFlags = threadContext->GetImplicitCallFlags();
  583. threadContext->SetImplicitCallFlags((Js::ImplicitCallFlags)(saveImplicitCallFlags | ImplicitCall_Accessor));
  584. if (!JavascriptOperators::GetOwnPropertyDescriptor(this, propertyId, requestContext, &proxyPropertyDescriptor))
  585. {
  586. PropertyDescriptor resultDescriptor;
  587. resultDescriptor.SetConfigurable(true);
  588. resultDescriptor.SetWritable(true);
  589. resultDescriptor.SetEnumerable(true);
  590. resultDescriptor.SetValue(value);
  591. return Js::JavascriptOperators::DefineOwnPropertyDescriptor(this, propertyId, resultDescriptor, true, requestContext);
  592. }
  593. else
  594. {
  595. // ES2017 Spec'd (9.1.9.1):
  596. // If existingDescriptor is not undefined, then
  597. // If IsAccessorDescriptor(existingDescriptor) is true, return false.
  598. // If existingDescriptor.[[Writable]] is false, return false.
  599. if (proxyPropertyDescriptor.IsAccessorDescriptor())
  600. {
  601. return FALSE;
  602. }
  603. if (proxyPropertyDescriptor.WritableSpecified() && !proxyPropertyDescriptor.IsWritable())
  604. {
  605. return FALSE;
  606. }
  607. proxyPropertyDescriptor.SetValue(value);
  608. proxyPropertyDescriptor.SetOriginal(nullptr);
  609. return Js::JavascriptOperators::DefineOwnPropertyDescriptor(this, propertyId, proxyPropertyDescriptor, true, requestContext);
  610. }
  611. }
  612. BOOL JavascriptProxy::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
  613. {
  614. const PropertyRecord* propertyRecord;
  615. GetScriptContext()->GetOrAddPropertyRecord(propertyNameString, &propertyRecord);
  616. return SetProperty(propertyRecord->GetPropertyId(), value, flags, info);
  617. }
  618. BOOL JavascriptProxy::SetInternalProperty(PropertyId internalPropertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
  619. {
  620. if (internalPropertyId == InternalPropertyIds::WeakMapKeyMap)
  621. {
  622. return __super::SetInternalProperty(internalPropertyId, value, flags, info);
  623. }
  624. return FALSE;
  625. }
  626. BOOL JavascriptProxy::InitProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
  627. {
  628. return SetProperty(propertyId, value, flags, info);
  629. }
  630. BOOL JavascriptProxy::EnsureProperty(PropertyId propertyId)
  631. {
  632. // proxy needs to be explicitly constructed. we don't have Ensure code path.
  633. Assert(FALSE);
  634. return false;
  635. }
  636. BOOL JavascriptProxy::EnsureNoRedeclProperty(PropertyId propertyId)
  637. {
  638. // proxy needs to be explicitly constructed. we don't have Ensure code path.
  639. Assert(FALSE);
  640. return false;
  641. }
  642. BOOL JavascriptProxy::SetPropertyWithAttributes(PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects)
  643. {
  644. // called from untrapped DefineProperty and from DOM side. I don't see this being used when the object is a proxy.
  645. Assert(FALSE);
  646. return false;
  647. }
  648. BOOL JavascriptProxy::InitPropertyScoped(PropertyId propertyId, Var value)
  649. {
  650. // proxy needs to be explicitly constructed. we don't have Ensure code path.
  651. Assert(FALSE);
  652. return false;
  653. }
  654. BOOL JavascriptProxy::InitFuncScoped(PropertyId propertyId, Var value)
  655. {
  656. // proxy needs to be explicitly constructed. we don't have Ensure code path.
  657. Assert(FALSE);
  658. return false;
  659. }
  660. BOOL JavascriptProxy::DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags)
  661. {
  662. PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
  663. // Reject implicit call
  664. ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
  665. if (threadContext->IsDisableImplicitCall())
  666. {
  667. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  668. return FALSE;
  669. }
  670. // Caller does not pass requestContext. Retrieve from host scriptContext stack.
  671. ScriptContext* requestContext =
  672. threadContext->GetPreviousHostScriptContext()->GetScriptContext();
  673. //1. Assert: IsPropertyKey(P) is true.
  674. //2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
  675. RecyclableObject * handlerObj = this->MarshalHandler(requestContext);
  676. //3. If handler is null, then throw a TypeError exception.
  677. //6. ReturnIfAbrupt(trap).
  678. if (handlerObj == nullptr)
  679. {
  680. // the proxy has been revoked; TypeError.
  681. if (!threadContext->RecordImplicitException())
  682. return FALSE;
  683. JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("deleteProperty"));
  684. }
  685. //4. Let target be the value of the[[ProxyTarget]] internal slot of O.
  686. RecyclableObject * targetObj = this->MarshalTarget(requestContext);
  687. //5. Let trap be the result of GetMethod(handler, "deleteProperty").
  688. JavascriptFunction* deleteMethod = GetMethodHelper(PropertyIds::deleteProperty, requestContext);
  689. //7. If trap is undefined, then
  690. //a.Return the result of calling the[[Delete]] internal method of target with argument P.
  691. Assert(!GetScriptContext()->IsHeapEnumInProgress());
  692. if (nullptr == deleteMethod)
  693. {
  694. uint32 indexVal;
  695. if (requestContext->IsNumericPropertyId(propertyId, &indexVal))
  696. {
  697. return targetObj->DeleteItem(indexVal, flags);
  698. }
  699. else
  700. {
  701. return targetObj->DeleteProperty(propertyId, flags);
  702. }
  703. }
  704. //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.
  705. //9. Let booleanTrapResult be ToBoolean(trapResult).
  706. //10. ReturnIfAbrupt(booleanTrapResult).
  707. //11. If booleanTrapResult is false, then return false.
  708. Var propertyName = GetName(requestContext, propertyId);
  709. Var deletePropertyResult = threadContext->ExecuteImplicitCall(deleteMethod, ImplicitCall_Accessor, [=]()->Js::Var
  710. {
  711. return CALL_FUNCTION(threadContext, deleteMethod, CallInfo(CallFlags_Value, 3), handlerObj, targetObj, propertyName);
  712. });
  713. BOOL trapResult = JavascriptConversion::ToBoolean(deletePropertyResult, requestContext);
  714. if (!trapResult)
  715. {
  716. return trapResult;
  717. }
  718. //12. Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
  719. //13. ReturnIfAbrupt(targetDesc).
  720. //14. If targetDesc is undefined, then return true.
  721. //15. If targetDesc.[[Configurable]] is false, then throw a TypeError exception.
  722. //16. Return true.
  723. PropertyDescriptor targetPropertyDescriptor;
  724. if (!Js::JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetPropertyDescriptor))
  725. {
  726. return TRUE;
  727. }
  728. if (!targetPropertyDescriptor.IsConfigurable())
  729. {
  730. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("deleteProperty"));
  731. }
  732. return TRUE;
  733. }
  734. BOOL JavascriptProxy::DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags)
  735. {
  736. PropertyRecord const *propertyRecord = nullptr;
  737. if (JavascriptOperators::ShouldTryDeleteProperty(this, propertyNameString, &propertyRecord))
  738. {
  739. Assert(propertyRecord);
  740. return DeleteProperty(propertyRecord->GetPropertyId(), flags);
  741. }
  742. return TRUE;
  743. }
  744. #if ENABLE_FIXED_FIELDS
  745. BOOL JavascriptProxy::IsFixedProperty(PropertyId propertyId)
  746. {
  747. // TODO: can we add support for fixed property? don't see a clear way to invalidate...
  748. return false;
  749. }
  750. #endif
  751. PropertyQueryFlags JavascriptProxy::HasItemQuery(uint32 index)
  752. {
  753. const PropertyRecord* propertyRecord;
  754. auto fn = [&](RecyclableObject* object)-> BOOL {
  755. return JavascriptOperators::HasItem(object, index);
  756. };
  757. auto getPropertyId = [&]() ->PropertyId {
  758. PropertyIdFromInt(index, &propertyRecord);
  759. return propertyRecord->GetPropertyId();
  760. };
  761. return JavascriptConversion::BooleanToPropertyQueryFlags(HasPropertyTrap(fn, getPropertyId));
  762. }
  763. BOOL JavascriptProxy::HasOwnItem(uint32 index)
  764. {
  765. const PropertyRecord* propertyRecord;
  766. auto fn = [&](RecyclableObject* object)-> BOOL {
  767. return JavascriptOperators::HasOwnItem(object, index);
  768. };
  769. auto getPropertyId = [&]() ->PropertyId {
  770. PropertyIdFromInt(index, &propertyRecord);
  771. return propertyRecord->GetPropertyId();
  772. };
  773. return HasPropertyTrap(fn, getPropertyId);
  774. }
  775. PropertyQueryFlags JavascriptProxy::GetItemQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext)
  776. {
  777. const PropertyRecord* propertyRecord;
  778. auto fn = [&](RecyclableObject* object)-> BOOL {
  779. return JavascriptOperators::GetItem(originalInstance, object, index, value, requestContext);
  780. };
  781. auto getPropertyId = [&]() ->PropertyId {
  782. PropertyIdFromInt(index, &propertyRecord);
  783. return propertyRecord->GetPropertyId();
  784. };
  785. PropertyDescriptor result;
  786. BOOL foundProperty = GetPropertyTrap(originalInstance, &result, fn, getPropertyId, requestContext);
  787. if (!foundProperty)
  788. {
  789. *value = requestContext->GetMissingItemResult();
  790. }
  791. else if (result.IsFromProxy())
  792. {
  793. *value = GetValueFromDescriptor(originalInstance, result, requestContext);
  794. }
  795. return JavascriptConversion::BooleanToPropertyQueryFlags(foundProperty);
  796. }
  797. PropertyQueryFlags JavascriptProxy::GetItemReferenceQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext)
  798. {
  799. const PropertyRecord* propertyRecord;
  800. auto fn = [&](RecyclableObject* object)-> BOOL {
  801. return JavascriptOperators::GetItem(originalInstance, object, index, value, requestContext);
  802. };
  803. auto getPropertyId = [&]() ->PropertyId {
  804. PropertyIdFromInt(index, &propertyRecord);
  805. return propertyRecord->GetPropertyId();
  806. };
  807. PropertyDescriptor result;
  808. BOOL foundProperty = GetPropertyTrap(originalInstance, &result, fn, getPropertyId, requestContext);
  809. if (!foundProperty)
  810. {
  811. *value = requestContext->GetMissingItemResult();
  812. }
  813. else if (result.IsFromProxy())
  814. {
  815. *value = GetValueFromDescriptor(originalInstance, result, requestContext);
  816. }
  817. return JavascriptConversion::BooleanToPropertyQueryFlags(foundProperty);
  818. }
  819. DescriptorFlags JavascriptProxy::GetItemSetter(uint32 index, Var* setterValueOrProxy, ScriptContext* requestContext)
  820. {
  821. *setterValueOrProxy = this;
  822. return DescriptorFlags::Proxy;
  823. }
  824. BOOL JavascriptProxy::SetItem(uint32 index, Var value, PropertyOperationFlags flags)
  825. {
  826. const PropertyRecord* propertyRecord;
  827. PropertyIdFromInt(index, &propertyRecord);
  828. return SetProperty(propertyRecord->GetPropertyId(), value, flags, nullptr);
  829. }
  830. BOOL JavascriptProxy::DeleteItem(uint32 index, PropertyOperationFlags flags)
  831. {
  832. const PropertyRecord* propertyRecord;
  833. PropertyIdFromInt(index, &propertyRecord);
  834. return DeleteProperty(propertyRecord->GetPropertyId(), flags);
  835. }
  836. // No change to foreign enumerator, just forward
  837. BOOL JavascriptProxy::GetEnumerator(JavascriptStaticEnumerator * enumerator, EnumeratorFlags flags, ScriptContext* requestContext, EnumeratorCache * enumeratorCache)
  838. {
  839. // Reject implicit call
  840. ThreadContext* threadContext = requestContext->GetThreadContext();
  841. if (threadContext->IsDisableImplicitCall())
  842. {
  843. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  844. return FALSE;
  845. }
  846. // 1. Assert: Either Type(V) is Object or Type(V) is Null.
  847. // 2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
  848. // 3. If handler is null, then throw a TypeError exception.
  849. if (this->handler == nullptr)
  850. {
  851. // the proxy has been revoked; TypeError.
  852. if (!threadContext->RecordImplicitException())
  853. return FALSE;
  854. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("ownKeys"));
  855. }
  856. struct ProxyOwnkeysEnumerator : public JavascriptEnumerator
  857. {
  858. typedef JsUtil::BaseHashSet<const char16*, Recycler> VisitedNamesHashSet;
  859. Field(VisitedNamesHashSet*) visited;
  860. Field(JavascriptArray*) trapResult;
  861. Field(JavascriptProxy*) proxy;
  862. FieldNoBarrier(ScriptContext*) scriptContext;
  863. Field(uint32) index;
  864. DEFINE_VTABLE_CTOR_ABSTRACT(ProxyOwnkeysEnumerator, JavascriptEnumerator)
  865. ProxyOwnkeysEnumerator(ScriptContext* scriptContext, JavascriptProxy* proxy, JavascriptArray* trapResult)
  866. :JavascriptEnumerator(scriptContext), scriptContext(scriptContext), proxy(proxy), trapResult(trapResult)
  867. {
  868. visited = RecyclerNew(scriptContext->GetRecycler(), VisitedNamesHashSet, scriptContext->GetRecycler());
  869. }
  870. virtual void Reset() override
  871. {
  872. index = 0;
  873. visited->Reset();
  874. }
  875. virtual JavascriptString * MoveAndGetNext(PropertyId& propertyId, PropertyAttributes* attributes = nullptr) override
  876. {
  877. propertyId = Constants::NoProperty;
  878. if (attributes != nullptr)
  879. {
  880. *attributes = PropertyEnumerable;
  881. }
  882. // 13.7.5.15 EnumerateObjectProperties(O) (https://tc39.github.io/ecma262/#sec-enumerate-object-properties)
  883. // for (let key of Reflect.ownKeys(obj)) {
  884. uint32 len = trapResult->GetLength();
  885. while (index < len)
  886. {
  887. Var var = trapResult->DirectGetItem(index++) ;
  888. if (var)
  889. {
  890. // if (typeof key === "string") {
  891. if (JavascriptString::Is(var))
  892. {
  893. JavascriptString* propertyName = JavascriptString::FromVar(var);
  894. // let desc = Reflect.getOwnPropertyDescriptor(obj, key);
  895. Js::PropertyDescriptor desc;
  896. BOOL ret = JavascriptOperators::GetOwnPropertyDescriptor(proxy, propertyName, scriptContext, &desc);
  897. // if (desc && !visited.has(key)) {
  898. if (ret && !visited->Contains(propertyName->GetSz()))
  899. {
  900. visited->Add(propertyName->GetSz());
  901. // if (desc.enumerable) yield key;
  902. if (desc.IsEnumerable())
  903. {
  904. return JavascriptString::FromVar(CrossSite::MarshalVar(
  905. scriptContext, propertyName, propertyName->GetScriptContext()));
  906. }
  907. }
  908. }
  909. }
  910. }
  911. return nullptr;
  912. }
  913. };
  914. JavascriptArray* trapResult = JavascriptOperators::GetOwnPropertyNames(this, requestContext);
  915. ProxyOwnkeysEnumerator* ownKeysEnum = RecyclerNew(requestContext->GetRecycler(), ProxyOwnkeysEnumerator, requestContext, this, trapResult);
  916. return enumerator->Initialize(ownKeysEnum, nullptr, nullptr, flags, requestContext, enumeratorCache);
  917. }
  918. BOOL JavascriptProxy::SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags)
  919. {
  920. // should be for __definegetter style usage. need to wait for clear spec what it means.
  921. Assert(FALSE);
  922. return false;
  923. }
  924. BOOL JavascriptProxy::Equals(__in Var other, __out BOOL* value, ScriptContext* requestContext)
  925. {
  926. //RecyclableObject* targetObj;
  927. if (this->target == nullptr)
  928. {
  929. // the proxy has been revoked; TypeError.
  930. JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("equal"));
  931. }
  932. // Reject implicit call
  933. ThreadContext* threadContext = requestContext->GetThreadContext();
  934. if (threadContext->IsDisableImplicitCall())
  935. {
  936. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  937. *value = FALSE;
  938. return FALSE;
  939. }
  940. *value = (other == this);
  941. return true;
  942. }
  943. BOOL JavascriptProxy::StrictEquals(__in Var other, __out BOOL* value, ScriptContext* requestContext)
  944. {
  945. *value = FALSE;
  946. //RecyclableObject* targetObj;
  947. if (this->target == nullptr)
  948. {
  949. // the proxy has been revoked; TypeError.
  950. JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("strict equal"));
  951. }
  952. // Reject implicit call
  953. ThreadContext* threadContext = requestContext->GetThreadContext();
  954. if (threadContext->IsDisableImplicitCall())
  955. {
  956. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  957. return FALSE;
  958. }
  959. *value = (other == this);
  960. return true;
  961. }
  962. BOOL JavascriptProxy::IsWritable(PropertyId propertyId)
  963. {
  964. PropertyDescriptor propertyDescriptor;
  965. if (!GetOwnPropertyDescriptor(this, propertyId, GetScriptContext(), &propertyDescriptor))
  966. {
  967. return FALSE;
  968. }
  969. // If property descriptor has getter/setter we should check if writable is specified before checking IsWritable
  970. return propertyDescriptor.WritableSpecified() ? propertyDescriptor.IsWritable() : FALSE;
  971. }
  972. BOOL JavascriptProxy::IsConfigurable(PropertyId propertyId)
  973. {
  974. Assert(FALSE);
  975. return target->IsConfigurable(propertyId);
  976. }
  977. BOOL JavascriptProxy::IsEnumerable(PropertyId propertyId)
  978. {
  979. Assert(FALSE);
  980. return target->IsEnumerable(propertyId);
  981. }
  982. BOOL JavascriptProxy::IsExtensible()
  983. {
  984. PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
  985. // Reject implicit call
  986. ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
  987. if (threadContext->IsDisableImplicitCall())
  988. {
  989. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  990. return FALSE;
  991. }
  992. // Caller does not pass requestContext. Retrieve from host scriptContext stack.
  993. ScriptContext* requestContext =
  994. threadContext->GetPreviousHostScriptContext()->GetScriptContext();
  995. //1. Let handler be the value of the[[ProxyHandler]] internal slot of O.
  996. Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
  997. //2. If handler is null, then throw a TypeError exception.
  998. if (handlerObj == nullptr)
  999. {
  1000. // the proxy has been revoked; TypeError.
  1001. if (!threadContext->RecordImplicitException())
  1002. return FALSE;
  1003. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("isExtensible"));
  1004. }
  1005. //3. Let target be the value of the[[ProxyTarget]] internal slot of O.
  1006. Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
  1007. //4. Let trap be the result of GetMethod(handler, "isExtensible").
  1008. //5. ReturnIfAbrupt(trap).
  1009. //6. If trap is undefined, then
  1010. //a.Return the result of calling the[[IsExtensible]] internal method of target.
  1011. //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.
  1012. //8. Let booleanTrapResult be ToBoolean(trapResult).
  1013. //9. ReturnIfAbrupt(booleanTrapResult).
  1014. //10. Let targetResult be the result of calling the[[IsExtensible]] internal method of target.
  1015. //11. ReturnIfAbrupt(targetResult).
  1016. //12. If SameValue(booleanTrapResult, targetResult) is false, then throw a TypeError exception.
  1017. //13. Return booleanTrapResult.
  1018. JavascriptFunction* isExtensibleMethod = GetMethodHelper(PropertyIds::isExtensible, requestContext);
  1019. Assert(!requestContext->IsHeapEnumInProgress());
  1020. if (nullptr == isExtensibleMethod)
  1021. {
  1022. return targetObj->IsExtensible();
  1023. }
  1024. Var isExtensibleResult = threadContext->ExecuteImplicitCall(isExtensibleMethod, ImplicitCall_Accessor, [=]()->Js::Var
  1025. {
  1026. return CALL_FUNCTION(threadContext, isExtensibleMethod, CallInfo(CallFlags_Value, 2), handlerObj, targetObj);
  1027. });
  1028. BOOL trapResult = JavascriptConversion::ToBoolean(isExtensibleResult, requestContext);
  1029. BOOL targetIsExtensible = targetObj->IsExtensible();
  1030. if (trapResult != targetIsExtensible)
  1031. {
  1032. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("isExtensible"));
  1033. }
  1034. return trapResult;
  1035. }
  1036. BOOL JavascriptProxy::PreventExtensions()
  1037. {
  1038. PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
  1039. // Reject implicit call
  1040. ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
  1041. if (threadContext->IsDisableImplicitCall())
  1042. {
  1043. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  1044. return FALSE;
  1045. }
  1046. // Caller does not pass requestContext. Retrieve from host scriptContext stack.
  1047. ScriptContext* requestContext =
  1048. threadContext->GetPreviousHostScriptContext()->GetScriptContext();
  1049. //1. Let handler be the value of the[[ProxyHandler]] internal slot of O.
  1050. Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
  1051. //2. If handler is null, then throw a TypeError exception.
  1052. if (handlerObj == nullptr)
  1053. {
  1054. // the proxy has been revoked; TypeError.
  1055. if (!threadContext->RecordImplicitException())
  1056. return FALSE;
  1057. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("preventExtensions"));
  1058. }
  1059. //3. Let target be the value of the[[ProxyTarget]] internal slot of O.
  1060. Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
  1061. //4. Let trap be the result of GetMethod(handler, "preventExtensions").
  1062. //5. ReturnIfAbrupt(trap).
  1063. //6. If trap is undefined, then
  1064. //a.Return the result of calling the[[PreventExtensions]] internal method of target.
  1065. //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.
  1066. JavascriptFunction* preventExtensionsMethod = GetMethodHelper(PropertyIds::preventExtensions, requestContext);
  1067. Assert(!GetScriptContext()->IsHeapEnumInProgress());
  1068. if (nullptr == preventExtensionsMethod)
  1069. {
  1070. return targetObj->PreventExtensions();
  1071. }
  1072. //8. Let booleanTrapResult be ToBoolean(trapResult)
  1073. //9. ReturnIfAbrupt(booleanTrapResult).
  1074. //10. Let targetIsExtensible be the result of calling the[[IsExtensible]] internal method of target.
  1075. //11. ReturnIfAbrupt(targetIsExtensible).
  1076. //12. If booleanTrapResult is true and targetIsExtensible is true, then throw a TypeError exception.
  1077. //13. Return booleanTrapResult.
  1078. Var preventExtensionsResult = threadContext->ExecuteImplicitCall(preventExtensionsMethod, ImplicitCall_Accessor, [=]()->Js::Var
  1079. {
  1080. return CALL_FUNCTION(threadContext, preventExtensionsMethod, CallInfo(CallFlags_Value, 2), handlerObj, targetObj);
  1081. });
  1082. BOOL trapResult = JavascriptConversion::ToBoolean(preventExtensionsResult, requestContext);
  1083. if (trapResult)
  1084. {
  1085. BOOL targetIsExtensible = targetObj->IsExtensible();
  1086. if (targetIsExtensible)
  1087. {
  1088. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("preventExtensions"));
  1089. }
  1090. }
  1091. return trapResult;
  1092. }
  1093. // 7.3.12 in ES 2015. While this should have been no observable behavior change. Till there is obvious change warrant this
  1094. // to be moved to JavascriptOperators, let's keep it in proxy only first.
  1095. BOOL JavascriptProxy::TestIntegrityLevel(IntegrityLevel integrityLevel, RecyclableObject* obj, ScriptContext* scriptContext)
  1096. {
  1097. //1. Assert: Type(O) is Object.
  1098. //2. Assert: level is either "sealed" or "frozen".
  1099. //3. Let status be IsExtensible(O).
  1100. //4. ReturnIfAbrupt(status).
  1101. //5. If status is true, then return false
  1102. //6. NOTE If the object is extensible, none of its properties are examined.
  1103. BOOL isExtensible = obj->IsExtensible();
  1104. if (isExtensible)
  1105. {
  1106. return FALSE;
  1107. }
  1108. // at this time this is called from proxy only; when we extend this to other objects, we need to handle the other codepath.
  1109. //7. Let keys be O.[[OwnPropertyKeys]]().
  1110. //8. ReturnIfAbrupt(keys).
  1111. Assert(JavascriptProxy::Is(obj));
  1112. JavascriptArray* resultArray = JavascriptOperators::GetOwnPropertyKeys(obj, scriptContext);
  1113. //9. Repeat for each element k of keys,
  1114. // a. Let currentDesc be O.[[GetOwnProperty]](k).
  1115. // b. ReturnIfAbrupt(currentDesc).
  1116. // c. If currentDesc is not undefined, then
  1117. // i. If currentDesc.[[Configurable]] is true, return false.
  1118. // ii. If level is "frozen" and IsDataDescriptor(currentDesc) is true, then
  1119. // 1. If currentDesc.[[Writable]] is true, return false.
  1120. Var itemVar;
  1121. bool writable = false;
  1122. bool configurable = false;
  1123. const PropertyRecord* propertyRecord;
  1124. PropertyDescriptor propertyDescriptor;
  1125. for (uint i = 0; i < resultArray->GetLength(); i++)
  1126. {
  1127. itemVar = resultArray->DirectGetItem(i);
  1128. 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)");
  1129. JavascriptConversion::ToPropertyKey(itemVar, scriptContext, &propertyRecord, nullptr);
  1130. PropertyId propertyId = propertyRecord->GetPropertyId();
  1131. if (JavascriptObject::GetOwnPropertyDescriptorHelper(obj, propertyId, scriptContext, propertyDescriptor))
  1132. {
  1133. configurable |= propertyDescriptor.IsConfigurable();
  1134. if (propertyDescriptor.IsDataDescriptor())
  1135. {
  1136. writable |= propertyDescriptor.IsWritable();
  1137. }
  1138. }
  1139. }
  1140. if (integrityLevel == IntegrityLevel::IntegrityLevel_frozen && writable)
  1141. {
  1142. return FALSE;
  1143. }
  1144. if (configurable)
  1145. {
  1146. return FALSE;
  1147. }
  1148. return TRUE;
  1149. }
  1150. BOOL JavascriptProxy::SetIntegrityLevel(IntegrityLevel integrityLevel, RecyclableObject* obj, ScriptContext* scriptContext)
  1151. {
  1152. //1. Assert: Type(O) is Object.
  1153. //2. Assert : level is either "sealed" or "frozen".
  1154. //3. Let status be O.[[PreventExtensions]]().
  1155. //4. ReturnIfAbrupt(status).
  1156. //5. If status is false, return false.
  1157. // at this time this is called from proxy only; when we extend this to other objects, we need to handle the other codepath.
  1158. Assert(JavascriptProxy::Is(obj));
  1159. if (obj->PreventExtensions() == FALSE)
  1160. return FALSE;
  1161. //6. Let keys be O.[[OwnPropertyKeys]]().
  1162. //7. ReturnIfAbrupt(keys).
  1163. JavascriptArray* resultArray = JavascriptOperators::GetOwnPropertyKeys(obj, scriptContext);
  1164. const PropertyRecord* propertyRecord;
  1165. if (integrityLevel == IntegrityLevel::IntegrityLevel_sealed)
  1166. {
  1167. //8. If level is "sealed", then
  1168. //a. Repeat for each element k of keys,
  1169. //i. Let status be DefinePropertyOrThrow(O, k, PropertyDescriptor{ [[Configurable]]: false }).
  1170. //ii. ReturnIfAbrupt(status).
  1171. PropertyDescriptor propertyDescriptor;
  1172. propertyDescriptor.SetConfigurable(false);
  1173. Var itemVar;
  1174. for (uint i = 0; i < resultArray->GetLength(); i++)
  1175. {
  1176. itemVar = resultArray->DirectGetItem(i);
  1177. 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)");
  1178. JavascriptConversion::ToPropertyKey(itemVar, scriptContext, &propertyRecord, nullptr);
  1179. PropertyId propertyId = propertyRecord->GetPropertyId();
  1180. JavascriptObject::DefineOwnPropertyHelper(obj, propertyId, propertyDescriptor, scriptContext);
  1181. }
  1182. }
  1183. else
  1184. {
  1185. //9.Else level is "frozen",
  1186. // a.Repeat for each element k of keys,
  1187. // i. Let currentDesc be O.[[GetOwnProperty]](k).
  1188. // ii. ReturnIfAbrupt(currentDesc).
  1189. // iii. If currentDesc is not undefined, then
  1190. // 1. If IsAccessorDescriptor(currentDesc) is true, then
  1191. // a. Let desc be the PropertyDescriptor{[[Configurable]]: false}.
  1192. // 2.Else,
  1193. // a. Let desc be the PropertyDescriptor { [[Configurable]]: false, [[Writable]]: false }.
  1194. // 3. Let status be DefinePropertyOrThrow(O, k, desc).
  1195. // 4. ReturnIfAbrupt(status).
  1196. Assert(integrityLevel == IntegrityLevel::IntegrityLevel_frozen);
  1197. PropertyDescriptor current, dataDescriptor, accessorDescriptor;
  1198. dataDescriptor.SetConfigurable(false);
  1199. dataDescriptor.SetWritable(false);
  1200. accessorDescriptor.SetConfigurable(false);
  1201. Var itemVar;
  1202. for (uint i = 0; i < resultArray->GetLength(); i++)
  1203. {
  1204. itemVar = resultArray->DirectGetItem(i);
  1205. 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)");
  1206. JavascriptConversion::ToPropertyKey(itemVar, scriptContext, &propertyRecord, nullptr);
  1207. PropertyId propertyId = propertyRecord->GetPropertyId();
  1208. PropertyDescriptor propertyDescriptor;
  1209. if (JavascriptObject::GetOwnPropertyDescriptorHelper(obj, propertyId, scriptContext, propertyDescriptor))
  1210. {
  1211. if (propertyDescriptor.IsDataDescriptor())
  1212. {
  1213. JavascriptObject::DefineOwnPropertyHelper(obj, propertyRecord->GetPropertyId(), dataDescriptor, scriptContext);
  1214. }
  1215. else if (propertyDescriptor.IsAccessorDescriptor())
  1216. {
  1217. JavascriptObject::DefineOwnPropertyHelper(obj, propertyRecord->GetPropertyId(), accessorDescriptor, scriptContext);
  1218. }
  1219. }
  1220. }
  1221. }
  1222. // 10. Return true
  1223. return TRUE;
  1224. }
  1225. BOOL JavascriptProxy::Seal()
  1226. {
  1227. return SetIntegrityLevel(IntegrityLevel::IntegrityLevel_sealed, this, this->GetScriptContext());
  1228. }
  1229. BOOL JavascriptProxy::Freeze()
  1230. {
  1231. return SetIntegrityLevel(IntegrityLevel::IntegrityLevel_frozen, this, this->GetScriptContext());
  1232. }
  1233. BOOL JavascriptProxy::IsSealed()
  1234. {
  1235. return TestIntegrityLevel(IntegrityLevel::IntegrityLevel_sealed, this, this->GetScriptContext());
  1236. }
  1237. BOOL JavascriptProxy::IsFrozen()
  1238. {
  1239. return TestIntegrityLevel(IntegrityLevel::IntegrityLevel_frozen, this, this->GetScriptContext());
  1240. }
  1241. BOOL JavascriptProxy::SetWritable(PropertyId propertyId, BOOL value)
  1242. {
  1243. Assert(FALSE);
  1244. return FALSE;
  1245. }
  1246. BOOL JavascriptProxy::SetConfigurable(PropertyId propertyId, BOOL value)
  1247. {
  1248. Assert(FALSE);
  1249. return FALSE;
  1250. }
  1251. BOOL JavascriptProxy::SetEnumerable(PropertyId propertyId, BOOL value)
  1252. {
  1253. Assert(FALSE);
  1254. return FALSE;
  1255. }
  1256. BOOL JavascriptProxy::SetAttributes(PropertyId propertyId, PropertyAttributes attributes)
  1257. {
  1258. Assert(FALSE);
  1259. return FALSE;
  1260. }
  1261. BOOL JavascriptProxy::HasInstance(Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache)
  1262. {
  1263. Var funcPrototype = JavascriptOperators::GetProperty(this, PropertyIds::prototype, scriptContext);
  1264. return JavascriptFunction::HasInstance(funcPrototype, instance, scriptContext, NULL, NULL);
  1265. }
  1266. JavascriptString* JavascriptProxy::GetClassName(ScriptContext * requestContext)
  1267. {
  1268. Assert(FALSE);
  1269. return nullptr;
  1270. }
  1271. RecyclableObject* JavascriptProxy::GetPrototypeSpecial()
  1272. {
  1273. PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
  1274. // Reject implicit call
  1275. ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
  1276. if (threadContext->IsDisableImplicitCall())
  1277. {
  1278. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  1279. return nullptr;
  1280. }
  1281. // Caller does not pass requestContext. Retrieve from host scriptContext stack.
  1282. ScriptContext* requestContext =
  1283. threadContext->GetPreviousHostScriptContext()->GetScriptContext();
  1284. Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
  1285. if (handlerObj == nullptr)
  1286. {
  1287. // the proxy has been revoked; TypeError.
  1288. if (!threadContext->RecordImplicitException())
  1289. return nullptr;
  1290. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("getPrototypeOf"));
  1291. }
  1292. Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
  1293. JavascriptFunction* getPrototypeOfMethod = GetMethodHelper(PropertyIds::getPrototypeOf, requestContext);
  1294. if (nullptr == getPrototypeOfMethod || GetScriptContext()->IsHeapEnumInProgress())
  1295. {
  1296. return RecyclableObject::FromVar(JavascriptObject::GetPrototypeOf(targetObj, requestContext));
  1297. }
  1298. Var getPrototypeOfResult = threadContext->ExecuteImplicitCall(getPrototypeOfMethod, ImplicitCall_Accessor, [=]()->Js::Var
  1299. {
  1300. return CALL_FUNCTION(threadContext, getPrototypeOfMethod, CallInfo(CallFlags_Value, 2), handlerObj, targetObj);
  1301. });
  1302. TypeId prototypeTypeId = JavascriptOperators::GetTypeId(getPrototypeOfResult);
  1303. if (!JavascriptOperators::IsObjectType(prototypeTypeId) && prototypeTypeId != TypeIds_Null)
  1304. {
  1305. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getPrototypeOf"));
  1306. }
  1307. if (!targetObj->IsExtensible() && !JavascriptConversion::SameValue(getPrototypeOfResult, targetObj->GetPrototype()))
  1308. {
  1309. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getPrototypeOf"));
  1310. }
  1311. return RecyclableObject::FromVar(getPrototypeOfResult);
  1312. }
  1313. RecyclableObject* JavascriptProxy::GetConfigurablePrototype(ScriptContext * requestContext)
  1314. {
  1315. // We should be using GetPrototypeSpecial for proxy object; never should come over here.
  1316. Assert(FALSE);
  1317. return nullptr;
  1318. }
  1319. void JavascriptProxy::RemoveFromPrototype(ScriptContext * requestContext)
  1320. {
  1321. Assert(FALSE);
  1322. }
  1323. void JavascriptProxy::AddToPrototype(ScriptContext * requestContext)
  1324. {
  1325. Assert(FALSE);
  1326. }
  1327. void JavascriptProxy::SetPrototype(RecyclableObject* newPrototype)
  1328. {
  1329. Assert(FALSE);
  1330. }
  1331. BOOL JavascriptProxy::SetPrototypeTrap(RecyclableObject* newPrototype, bool shouldThrow,
  1332. ScriptContext * requestContext)
  1333. {
  1334. PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
  1335. Assert(JavascriptOperators::IsObjectOrNull(newPrototype));
  1336. // Reject implicit call
  1337. ThreadContext* threadContext = requestContext->GetThreadContext();
  1338. if (threadContext->IsDisableImplicitCall())
  1339. {
  1340. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  1341. return FALSE;
  1342. }
  1343. //1. Assert: Either Type(V) is Object or Type(V) is Null.
  1344. //2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
  1345. Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
  1346. //3. If handler is null, then throw a TypeError exception.
  1347. if (handlerObj == nullptr)
  1348. {
  1349. // the proxy has been revoked; TypeError.
  1350. if (shouldThrow)
  1351. {
  1352. if (!threadContext->RecordImplicitException())
  1353. return FALSE;
  1354. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("setPrototypeOf"));
  1355. }
  1356. }
  1357. //4. Let target be the value of the[[ProxyTarget]] internal slot of O.
  1358. Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
  1359. //5. Let trap be the result of GetMethod(handler, "setPrototypeOf").
  1360. //6. ReturnIfAbrupt(trap).
  1361. //7. If trap is undefined, then
  1362. //a.Return the result of calling the[[SetPrototypeOf]] internal method of target with argument V.
  1363. JavascriptFunction* setPrototypeOfMethod = GetMethodHelper(PropertyIds::setPrototypeOf, requestContext);
  1364. Assert(!GetScriptContext()->IsHeapEnumInProgress());
  1365. if (nullptr == setPrototypeOfMethod)
  1366. {
  1367. JavascriptObject::ChangePrototype(targetObj, newPrototype, shouldThrow, requestContext);
  1368. return TRUE;
  1369. }
  1370. //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.
  1371. Var setPrototypeResult = threadContext->ExecuteImplicitCall(setPrototypeOfMethod, ImplicitCall_Accessor, [=]()->Js::Var
  1372. {
  1373. return CALL_FUNCTION(threadContext, setPrototypeOfMethod, CallInfo(CallFlags_Value, 3), handlerObj, targetObj, newPrototype);
  1374. });
  1375. //9. Let booleanTrapResult be ToBoolean(trapResult).
  1376. //10. ReturnIfAbrupt(booleanTrapResult).
  1377. //11. Let extensibleTarget be the result of IsExtensible(target).
  1378. //12. ReturnIfAbrupt(extensibleTarget).
  1379. //13. If extensibleTarget is true, then return booleanTrapResult.
  1380. //14. Let targetProto be the result of calling the[[GetPrototypeOf]] internal method of target.
  1381. //15. ReturnIfAbrupt(targetProto).
  1382. //16. If booleanTrapResult is true and SameValue(V, targetProto) is false, then throw a TypeError exception.
  1383. //17. Return booleanTrapResult.
  1384. BOOL prototypeSetted = JavascriptConversion::ToBoolean(setPrototypeResult, requestContext);
  1385. BOOL isExtensible = targetObj->IsExtensible();
  1386. if (isExtensible)
  1387. {
  1388. if (!prototypeSetted && shouldThrow)
  1389. {
  1390. JavascriptError::ThrowTypeError(requestContext, JSERR_ProxyTrapReturnedFalse, _u("setPrototypeOf"));
  1391. }
  1392. return prototypeSetted;
  1393. }
  1394. Var targetProto = targetObj->GetPrototype();
  1395. if (!JavascriptConversion::SameValue(targetProto, newPrototype))
  1396. {
  1397. if (shouldThrow)
  1398. {
  1399. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("setPrototypeOf"));
  1400. }
  1401. return FALSE;
  1402. }
  1403. return TRUE;
  1404. }
  1405. Var JavascriptProxy::ToString(ScriptContext* scriptContext)
  1406. {
  1407. //RecyclableObject* targetObj;
  1408. if (this->handler == nullptr)
  1409. {
  1410. ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
  1411. // the proxy has been revoked; TypeError.
  1412. if (!threadContext->RecordImplicitException())
  1413. return nullptr;
  1414. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("toString"));
  1415. }
  1416. return JavascriptObject::ToStringHelper(target, scriptContext);
  1417. }
  1418. BOOL JavascriptProxy::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  1419. {
  1420. //RecyclableObject* targetObj;
  1421. if (this->handler == nullptr)
  1422. {
  1423. ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
  1424. // the proxy has been revoked; TypeError.
  1425. if (!threadContext->RecordImplicitException())
  1426. return FALSE;
  1427. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("getTypeString"));
  1428. }
  1429. return target->GetDiagTypeString(stringBuilder, requestContext);
  1430. }
  1431. RecyclableObject* JavascriptProxy::ToObject(ScriptContext * requestContext)
  1432. {
  1433. //RecyclableObject* targetObj;
  1434. if (this->handler == nullptr)
  1435. {
  1436. ThreadContext* threadContext = GetScriptContext()->GetThreadContext();
  1437. // the proxy has been revoked; TypeError.
  1438. if (!threadContext->RecordImplicitException())
  1439. return nullptr;
  1440. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("toObject"));
  1441. }
  1442. return __super::ToObject(requestContext);
  1443. }
  1444. Var JavascriptProxy::GetTypeOfString(ScriptContext* requestContext)
  1445. {
  1446. if (this->handler == nullptr)
  1447. {
  1448. // even if handler is nullptr, return typeof as "object"
  1449. return requestContext->GetLibrary()->GetObjectTypeDisplayString();
  1450. }
  1451. // if exotic object has [[Call]] we should return "function", otherwise return "object"
  1452. if (JavascriptFunction::Is(this->target))
  1453. {
  1454. return requestContext->GetLibrary()->GetFunctionTypeDisplayString();
  1455. }
  1456. else
  1457. {
  1458. return requestContext->GetLibrary()->GetObjectTypeDisplayString();
  1459. }
  1460. }
  1461. BOOL JavascriptProxy::GetOwnPropertyDescriptor(RecyclableObject* obj, PropertyId propertyId, ScriptContext* requestContext, PropertyDescriptor* propertyDescriptor)
  1462. {
  1463. JavascriptProxy* proxy = JavascriptProxy::FromVar(obj);
  1464. return proxy->GetPropertyDescriptorTrap(propertyId, propertyDescriptor, requestContext);
  1465. }
  1466. BOOL JavascriptProxy::DefineOwnPropertyDescriptor(RecyclableObject* obj, PropertyId propId, const PropertyDescriptor& descriptor, bool throwOnError, ScriptContext* requestContext)
  1467. {
  1468. PROBE_STACK(requestContext, Js::Constants::MinStackDefault);
  1469. // Reject implicit call
  1470. ThreadContext* threadContext = requestContext->GetThreadContext();
  1471. if (threadContext->IsDisableImplicitCall())
  1472. {
  1473. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  1474. return FALSE;
  1475. }
  1476. JavascriptProxy* proxy = JavascriptProxy::FromVar(obj);
  1477. //1. Assert: IsPropertyKey(P) is true.
  1478. //2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
  1479. RecyclableObject *handlerObj = proxy->MarshalHandler(requestContext);
  1480. //3. If handler is null, then throw a TypeError exception.
  1481. if (handlerObj == nullptr)
  1482. {
  1483. // the proxy has been revoked; TypeError.
  1484. if (!threadContext->RecordImplicitException())
  1485. return FALSE;
  1486. JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("definePropertyDescriptor"));
  1487. }
  1488. //4. Let target be the value of the[[ProxyTarget]] internal slot of O.
  1489. RecyclableObject *targetObj = proxy->MarshalTarget(requestContext);
  1490. //5. Let trap be the result of GetMethod(handler, "defineProperty").
  1491. //6. ReturnIfAbrupt(trap).
  1492. //7. If trap is undefined, then
  1493. //a.Return the result of calling the[[DefineOwnProperty]] internal method of target with arguments P and Desc.
  1494. JavascriptFunction* defineOwnPropertyMethod = proxy->GetMethodHelper(PropertyIds::defineProperty, requestContext);
  1495. Assert(!requestContext->IsHeapEnumInProgress());
  1496. if (nullptr == defineOwnPropertyMethod)
  1497. {
  1498. return JavascriptOperators::DefineOwnPropertyDescriptor(targetObj, propId, descriptor, throwOnError, requestContext);
  1499. }
  1500. //8. Let descObj be FromPropertyDescriptor(Desc).
  1501. //9. NOTE If Desc was originally generated from an object using ToPropertyDescriptor, then descObj will be that original object.
  1502. //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.
  1503. //11. Let booleanTrapResult be ToBoolean(trapResult).
  1504. //12. ReturnIfAbrupt(booleanTrapResult).
  1505. //13. If booleanTrapResult is false, then return false.
  1506. //14. Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
  1507. //15. ReturnIfAbrupt(targetDesc).
  1508. Var descVar = descriptor.GetOriginal();
  1509. if (descVar == nullptr)
  1510. {
  1511. descVar = JavascriptOperators::FromPropertyDescriptor(descriptor, requestContext);
  1512. }
  1513. Var propertyName = GetName(requestContext, propId);
  1514. Var definePropertyResult = threadContext->ExecuteImplicitCall(defineOwnPropertyMethod, ImplicitCall_Accessor, [=]()->Js::Var
  1515. {
  1516. return CALL_FUNCTION(threadContext, defineOwnPropertyMethod, CallInfo(CallFlags_Value, 4), handlerObj, targetObj, propertyName, descVar);
  1517. });
  1518. BOOL defineResult = JavascriptConversion::ToBoolean(definePropertyResult, requestContext);
  1519. if (!defineResult)
  1520. {
  1521. return defineResult;
  1522. }
  1523. //16. Let extensibleTarget be the result of IsExtensible(target).
  1524. //17. ReturnIfAbrupt(extensibleTarget).
  1525. //18. If Desc has a[[Configurable]] field and if Desc.[[Configurable]] is false, then
  1526. // a.Let settingConfigFalse be true.
  1527. //19. Else let settingConfigFalse be false.
  1528. //20. If targetDesc is undefined, then
  1529. // a.If extensibleTarget is false, then throw a TypeError exception.
  1530. // b.If settingConfigFalse is true, then throw a TypeError exception.
  1531. //21. Else targetDesc is not undefined,
  1532. // a.If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, then throw a TypeError exception.
  1533. // b.If settingConfigFalse is true and targetDesc.[[Configurable]] is true, then throw a TypeError exception.
  1534. //22. Return true.
  1535. PropertyDescriptor targetDescriptor;
  1536. BOOL hasProperty = JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propId, requestContext, &targetDescriptor);
  1537. BOOL isExtensible = targetObj->IsExtensible();
  1538. BOOL settingConfigFalse = (descriptor.ConfigurableSpecified() && !descriptor.IsConfigurable());
  1539. if (!hasProperty)
  1540. {
  1541. if (!isExtensible || settingConfigFalse)
  1542. {
  1543. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("defineProperty"));
  1544. }
  1545. }
  1546. else
  1547. {
  1548. if (!JavascriptOperators::IsCompatiblePropertyDescriptor(descriptor, hasProperty? &targetDescriptor : nullptr, !!isExtensible, true, requestContext))
  1549. {
  1550. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("defineProperty"));
  1551. }
  1552. if (settingConfigFalse && targetDescriptor.IsConfigurable())
  1553. {
  1554. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("defineProperty"));
  1555. }
  1556. }
  1557. return TRUE;
  1558. }
  1559. BOOL JavascriptProxy::SetPropertyTrap(Var receiver, SetPropertyTrapKind setPropertyTrapKind, Js::JavascriptString * propertyNameString, Var newValue, ScriptContext* requestContext)
  1560. {
  1561. const PropertyRecord* propertyRecord;
  1562. requestContext->GetOrAddPropertyRecord(propertyNameString, &propertyRecord);
  1563. return SetPropertyTrap(receiver, setPropertyTrapKind, propertyRecord->GetPropertyId(), newValue, requestContext);
  1564. }
  1565. BOOL JavascriptProxy::SetPropertyTrap(Var receiver, SetPropertyTrapKind setPropertyTrapKind, PropertyId propertyId, Var newValue, ScriptContext* requestContext, BOOL skipPrototypeCheck)
  1566. {
  1567. PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
  1568. // Reject implicit call
  1569. ThreadContext* threadContext = requestContext->GetThreadContext();
  1570. if (threadContext->IsDisableImplicitCall())
  1571. {
  1572. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  1573. return FALSE;
  1574. }
  1575. //1. Assert: IsPropertyKey(P) is true.
  1576. //2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
  1577. Js::RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
  1578. //3. If handler is null, then throw a TypeError exception.
  1579. if (handlerObj == nullptr)
  1580. {
  1581. // the proxy has been revoked; TypeError.
  1582. if (!threadContext->RecordImplicitException())
  1583. return FALSE;
  1584. JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("set"));
  1585. }
  1586. //4. Let target be the value of the[[ProxyTarget]] internal slot of O.
  1587. Js::RecyclableObject *targetObj = this->MarshalTarget(requestContext);
  1588. //5. Let trap be the result of GetMethod(handler, "set").
  1589. //6. ReturnIfAbrupt(trap).
  1590. //7. If trap is undefined, then
  1591. //a.Return the result of calling the[[Set]] internal method of target with arguments P, V, and Receiver.
  1592. JavascriptFunction* setMethod = GetMethodHelper(PropertyIds::set, requestContext);
  1593. Assert(!GetScriptContext()->IsHeapEnumInProgress());
  1594. if (nullptr == setMethod)
  1595. {
  1596. PropertyValueInfo info;
  1597. switch (setPropertyTrapKind)
  1598. {
  1599. case SetPropertyTrapKind::SetItemOnTaggedNumberKind:
  1600. {
  1601. uint32 indexVal;
  1602. BOOL isNumericPropertyId = requestContext->IsNumericPropertyId(propertyId, &indexVal);
  1603. Assert(isNumericPropertyId);
  1604. return JavascriptOperators::SetItemOnTaggedNumber(receiver, targetObj, indexVal, newValue, requestContext, PropertyOperationFlags::PropertyOperation_None);
  1605. }
  1606. case SetPropertyTrapKind::SetPropertyOnTaggedNumberKind:
  1607. return JavascriptOperators::SetPropertyOnTaggedNumber(receiver, targetObj, propertyId, newValue, requestContext, PropertyOperation_None);
  1608. case SetPropertyTrapKind::SetPropertyKind:
  1609. return JavascriptOperators::SetProperty(receiver, targetObj, propertyId, newValue, requestContext);
  1610. case SetPropertyTrapKind::SetItemKind:
  1611. {
  1612. uint32 indexVal;
  1613. BOOL isNumericPropertyId = requestContext->IsNumericPropertyId(propertyId, &indexVal);
  1614. Assert(isNumericPropertyId);
  1615. return JavascriptOperators::SetItem(receiver, targetObj, indexVal, newValue, requestContext, PropertyOperationFlags::PropertyOperation_None, skipPrototypeCheck);
  1616. }
  1617. case SetPropertyTrapKind::SetPropertyWPCacheKind:
  1618. {
  1619. PropertyValueInfo propertyValueInfo;
  1620. return JavascriptOperators::SetPropertyWPCache(receiver, targetObj, propertyId, newValue, requestContext, PropertyOperationFlags::PropertyOperation_None, &propertyValueInfo);
  1621. }
  1622. default:
  1623. AnalysisAssert(FALSE);
  1624. }
  1625. }
  1626. //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.
  1627. //9. Let booleanTrapResult be ToBoolean(trapResult).
  1628. //10. ReturnIfAbrupt(booleanTrapResult).
  1629. //11. If booleanTrapResult is false, then return false.
  1630. Var propertyName = GetName(requestContext, propertyId);
  1631. Var setPropertyResult = threadContext->ExecuteImplicitCall(setMethod, ImplicitCall_Accessor, [=]()->Js::Var
  1632. {
  1633. return CALL_FUNCTION(threadContext, setMethod, CallInfo(CallFlags_Value, 5), handlerObj, targetObj, propertyName, newValue, receiver);
  1634. });
  1635. BOOL setResult = JavascriptConversion::ToBoolean(setPropertyResult, requestContext);
  1636. if (!setResult)
  1637. {
  1638. return setResult;
  1639. }
  1640. //12. Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
  1641. //13. ReturnIfAbrupt(targetDesc).
  1642. //14. If targetDesc is not undefined, then
  1643. //a.If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is false, then
  1644. //i.If SameValue(V, targetDesc.[[Value]]) is false, then throw a TypeError exception.
  1645. //b.If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false, then
  1646. //i.If targetDesc.[[Set]] is undefined, then throw a TypeError exception.
  1647. //15. Return true
  1648. PropertyDescriptor targetDescriptor;
  1649. BOOL hasProperty;
  1650. hasProperty = JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetDescriptor);
  1651. if (hasProperty)
  1652. {
  1653. if (targetDescriptor.ValueSpecified())
  1654. {
  1655. if (!targetDescriptor.IsConfigurable() && !targetDescriptor.IsWritable() &&
  1656. !JavascriptConversion::SameValue(newValue, targetDescriptor.GetValue()))
  1657. {
  1658. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("set"));
  1659. }
  1660. }
  1661. else
  1662. {
  1663. if (!targetDescriptor.IsConfigurable() && targetDescriptor.GetSetter() == requestContext->GetLibrary()->GetDefaultAccessorFunction())
  1664. {
  1665. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("set"));
  1666. }
  1667. }
  1668. }
  1669. return TRUE;
  1670. }
  1671. JavascriptFunction* JavascriptProxy::GetMethodHelper(PropertyId methodId, ScriptContext* requestContext)
  1672. {
  1673. //2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
  1674. //3. If handler is null, then throw a TypeError exception.
  1675. if (this->handler == nullptr)
  1676. {
  1677. // the proxy has been revoked; TypeError.
  1678. JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, requestContext->GetPropertyName(methodId)->GetBuffer());
  1679. }
  1680. Var varMethod;
  1681. //5. Let trap be the result of GetMethod(handler, "getOwnPropertyDescriptor").
  1682. //6. ReturnIfAbrupt(trap).
  1683. //7.3.9 GetMethod(V, P)
  1684. // The abstract operation GetMethod is used to get the value of a specific property of an ECMAScript language value when the value of the
  1685. // 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
  1686. // property key. This abstract operation performs the following steps:
  1687. // 1. Assert: IsPropertyKey(P) is true.
  1688. // 2. Let func be ? GetV(V, P).
  1689. // 3. If func is either undefined or null, return undefined.
  1690. // 4. If IsCallable(func) is false, throw a TypeError exception.
  1691. // 5. Return func.
  1692. BOOL result = JavascriptOperators::GetPropertyReference(handler, methodId, &varMethod, requestContext);
  1693. if (!result || JavascriptOperators::IsUndefinedOrNull(varMethod))
  1694. {
  1695. return nullptr;
  1696. }
  1697. if (!JavascriptFunction::Is(varMethod))
  1698. {
  1699. JavascriptError::ThrowTypeError(requestContext, JSERR_NeedFunction, requestContext->GetPropertyName(methodId)->GetBuffer());
  1700. }
  1701. JavascriptFunction* function = JavascriptFunction::FromVar(varMethod);
  1702. return JavascriptFunction::FromVar(CrossSite::MarshalVar(requestContext,
  1703. function, function->GetScriptContext()));
  1704. }
  1705. Var JavascriptProxy::GetValueFromDescriptor(Var instance, PropertyDescriptor propertyDescriptor, ScriptContext* requestContext)
  1706. {
  1707. if (propertyDescriptor.ValueSpecified())
  1708. {
  1709. return CrossSite::MarshalVar(requestContext, propertyDescriptor.GetValue());
  1710. }
  1711. if (propertyDescriptor.GetterSpecified())
  1712. {
  1713. return JavascriptOperators::CallGetter(RecyclableObject::FromVar(propertyDescriptor.GetGetter()), instance, requestContext);
  1714. }
  1715. Assert(FALSE);
  1716. return requestContext->GetLibrary()->GetUndefined();
  1717. }
  1718. void JavascriptProxy::PropertyIdFromInt(uint32 index, PropertyRecord const** propertyRecord)
  1719. {
  1720. char16 buffer[22];
  1721. int pos = TaggedInt::ToBuffer(index, buffer, _countof(buffer));
  1722. GetScriptContext()->GetOrAddPropertyRecord((LPCWSTR)buffer + pos, (_countof(buffer) - 1) - pos, propertyRecord);
  1723. }
  1724. Var JavascriptProxy::GetName(ScriptContext* requestContext, PropertyId propertyId)
  1725. {
  1726. const PropertyRecord* propertyRecord = requestContext->GetThreadContext()->GetPropertyName(propertyId);
  1727. Var name;
  1728. if (propertyRecord->IsSymbol())
  1729. {
  1730. name = requestContext->GetSymbol(propertyRecord);
  1731. }
  1732. else
  1733. {
  1734. name = requestContext->GetPropertyString(propertyRecord);
  1735. }
  1736. return name;
  1737. }
  1738. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  1739. PropertyId JavascriptProxy::EnsureHandlerPropertyId(ScriptContext* scriptContext)
  1740. {
  1741. ThreadContext* threadContext = scriptContext->GetThreadContext();
  1742. if (threadContext->handlerPropertyId == Js::Constants::NoProperty)
  1743. {
  1744. LPCWSTR autoProxyName;
  1745. if (threadContext->GetAutoProxyName() != nullptr)
  1746. {
  1747. autoProxyName = threadContext->GetAutoProxyName();
  1748. }
  1749. else
  1750. {
  1751. autoProxyName = Js::Configuration::Global.flags.autoProxy;
  1752. }
  1753. threadContext->handlerPropertyId = threadContext->GetOrAddPropertyRecordBind(
  1754. JsUtil::CharacterBuffer<WCHAR>(autoProxyName, static_cast<charcount_t>(wcslen(autoProxyName))))->GetPropertyId();
  1755. }
  1756. return threadContext->handlerPropertyId;
  1757. }
  1758. RecyclableObject* JavascriptProxy::AutoProxyWrapper(Var obj)
  1759. {
  1760. RecyclableObject* object = RecyclableObject::FromVar(obj);
  1761. if (!JavascriptOperators::IsObject(object) || JavascriptProxy::Is(object))
  1762. {
  1763. return object;
  1764. }
  1765. ScriptContext* scriptContext = object->GetScriptContext();
  1766. if (!scriptContext->GetThreadContext()->IsScriptActive())
  1767. {
  1768. return object;
  1769. }
  1770. if (!scriptContext->GetConfig()->IsES6ProxyEnabled())
  1771. {
  1772. return object;
  1773. }
  1774. Assert(Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag));
  1775. PropertyId handlerId = EnsureHandlerPropertyId(scriptContext);
  1776. GlobalObject* globalObject = scriptContext->GetLibrary()->GetGlobalObject();
  1777. Var handler = nullptr;
  1778. if (!JavascriptOperators::GetProperty(globalObject, handlerId, &handler, scriptContext))
  1779. {
  1780. handler = scriptContext->GetLibrary()->CreateObject();
  1781. JavascriptOperators::SetProperty(globalObject, globalObject, handlerId, handler, scriptContext);
  1782. }
  1783. CallInfo callInfo(CallFlags_Value, 3);
  1784. Var varArgs[3];
  1785. Js::Arguments arguments(callInfo, varArgs);
  1786. varArgs[0] = scriptContext->GetLibrary()->GetProxyConstructor();
  1787. varArgs[1] = object;
  1788. varArgs[2] = handler;
  1789. return Create(scriptContext, arguments);
  1790. }
  1791. #endif
  1792. Var JavascriptProxy::ConstructorTrap(Arguments args, ScriptContext* scriptContext, const Js::AuxArray<uint32> *spreadIndices)
  1793. {
  1794. PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
  1795. Var functionResult;
  1796. if (spreadIndices != nullptr)
  1797. {
  1798. functionResult = JavascriptFunction::CallSpreadFunction(this, args, spreadIndices);
  1799. }
  1800. else
  1801. {
  1802. functionResult = JavascriptFunction::CallFunction<true>(this, this->GetEntryPoint(), args);
  1803. }
  1804. return functionResult;
  1805. }
  1806. Var JavascriptProxy::FunctionCallTrap(RecyclableObject* function, CallInfo callInfo, ...)
  1807. {
  1808. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1809. ARGUMENTS(args, callInfo);
  1810. ScriptContext* scriptContext = function->GetScriptContext();
  1811. BOOL hasOverridingNewTarget = args.HasNewTarget();
  1812. bool isCtorSuperCall = JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
  1813. bool isNewCall = args.IsNewCall() || hasOverridingNewTarget;
  1814. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  1815. if (!JavascriptProxy::Is(function))
  1816. {
  1817. if (args.Info.Flags & CallFlags_New)
  1818. {
  1819. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction, _u("construct"));
  1820. }
  1821. else
  1822. {
  1823. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction, _u("call"));
  1824. }
  1825. }
  1826. Var newTarget = nullptr;
  1827. JavascriptProxy* proxy = JavascriptProxy::FromVar(function);
  1828. Js::RecyclableObject *handlerObj = proxy->handler;
  1829. Js::RecyclableObject *targetObj = proxy->target;
  1830. JavascriptFunction* callMethod;
  1831. Assert(!scriptContext->IsHeapEnumInProgress());
  1832. // To conform with ES6 spec 7.3.13
  1833. if (hasOverridingNewTarget)
  1834. {
  1835. newTarget = args.Values[callInfo.Count];
  1836. }
  1837. else
  1838. {
  1839. newTarget = proxy;
  1840. }
  1841. if (args.Info.Flags & CallFlags_New)
  1842. {
  1843. callMethod = proxy->GetMethodHelper(PropertyIds::construct, scriptContext);
  1844. }
  1845. else
  1846. {
  1847. callMethod = proxy->GetMethodHelper(PropertyIds::apply, scriptContext);
  1848. }
  1849. if (!JavascriptConversion::IsCallable(targetObj))
  1850. {
  1851. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction, _u("call"));
  1852. }
  1853. if (nullptr == callMethod)
  1854. {
  1855. // newCount is ushort. If args count is greater than or equal to 65535, an integer
  1856. // too many arguments
  1857. if (args.Info.Count >= USHORT_MAX) //check against CallInfo::kMaxCountArgs if newCount is ever made int
  1858. {
  1859. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgListTooLarge);
  1860. }
  1861. // in [[construct]] case, we don't need to check if the function is a constructor: the function should throw there.
  1862. Var newThisObject = nullptr;
  1863. if (args.Info.Flags & CallFlags_New)
  1864. {
  1865. if (!JavascriptOperators::IsConstructor(targetObj))
  1866. {
  1867. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedFunction, _u("construct"));
  1868. }
  1869. // args.Values[0] will be null in the case where NewTarget is initially provided by proxy.
  1870. if (!isCtorSuperCall || !args.Values[0])
  1871. {
  1872. newThisObject = JavascriptOperators::NewScObjectNoCtor(targetObj, scriptContext);
  1873. args.Values[0] = newThisObject;
  1874. }
  1875. else
  1876. {
  1877. newThisObject = args.Values[0];
  1878. }
  1879. }
  1880. ushort newCount = (ushort)args.Info.Count;
  1881. if (isNewCall)
  1882. {
  1883. newCount++;
  1884. if (!newCount)
  1885. {
  1886. ::Math::DefaultOverflowPolicy();
  1887. }
  1888. }
  1889. AnalysisAssert(newCount >= (ushort)args.Info.Count);
  1890. Var* newValues;
  1891. const unsigned STACK_ARGS_ALLOCA_THRESHOLD = 8; // Number of stack args we allow before using _alloca
  1892. Var stackArgs[STACK_ARGS_ALLOCA_THRESHOLD];
  1893. if (newCount > STACK_ARGS_ALLOCA_THRESHOLD)
  1894. {
  1895. PROBE_STACK(scriptContext, newCount * sizeof(Var) + Js::Constants::MinStackDefault); // args + function call
  1896. newValues = (Var*)_alloca(newCount * sizeof(Var));
  1897. }
  1898. else
  1899. {
  1900. newValues = stackArgs;
  1901. }
  1902. CallInfo calleeInfo((CallFlags)(args.Info.Flags), args.Info.Count);
  1903. if (isNewCall)
  1904. {
  1905. calleeInfo.Flags = (CallFlags)(calleeInfo.Flags | CallFlags_ExtraArg | CallFlags_NewTarget);
  1906. }
  1907. for (ushort argCount = 0; argCount < (ushort)args.Info.Count; argCount++)
  1908. {
  1909. AnalysisAssert(newCount >= ((ushort)args.Info.Count));
  1910. AnalysisAssert(argCount < newCount);
  1911. AnalysisAssert(argCount < (ushort)args.Info.Count);
  1912. AnalysisAssert(sizeof(Var*) == sizeof(void*));
  1913. AnalysisAssert(sizeof(Var*) * argCount < sizeof(void*) * newCount);
  1914. #pragma prefast(suppress:__WARNING_WRITE_OVERRUN, "This is a false positive, and all of the above analysis asserts still didn't convince prefast of that.")
  1915. newValues[argCount] = args.Values[argCount];
  1916. }
  1917. if (isNewCall)
  1918. {
  1919. AnalysisAssert(newCount == ((ushort)args.Info.Count) + 1);
  1920. newValues[args.Info.Count] = newTarget;
  1921. }
  1922. Js::Arguments arguments(calleeInfo, newValues);
  1923. Var aReturnValue = nullptr;
  1924. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  1925. {
  1926. aReturnValue = JavascriptFunction::CallFunction<true>(targetObj, targetObj->GetEntryPoint(), arguments);
  1927. }
  1928. END_SAFE_REENTRANT_CALL
  1929. // If this is constructor call, return the actual object instead of function result
  1930. if ((callInfo.Flags & CallFlags_New) && !JavascriptOperators::IsObject(aReturnValue))
  1931. {
  1932. aReturnValue = newThisObject;
  1933. }
  1934. return aReturnValue;
  1935. }
  1936. JavascriptArray* argList = scriptContext->GetLibrary()->CreateArray(callInfo.Count - 1);
  1937. for (uint i = 1; i < callInfo.Count; i++)
  1938. {
  1939. argList->DirectSetItemAt(i - 1, args[i]);
  1940. }
  1941. Var varArgs[4];
  1942. CallInfo calleeInfo(CallFlags_Value, 4);
  1943. Js::Arguments arguments(calleeInfo, varArgs);
  1944. varArgs[0] = handlerObj;
  1945. varArgs[1] = targetObj;
  1946. if (args.Info.Flags & CallFlags_New)
  1947. {
  1948. if (!JavascriptOperators::IsConstructor(targetObj))
  1949. {
  1950. JavascriptError::ThrowTypeError(scriptContext, JSERR_NotAConstructor);
  1951. }
  1952. varArgs[2] = argList;
  1953. // 1st preference - overridden newTarget
  1954. // 2nd preference - 'this' in case of super() call
  1955. // 3rd preference - newTarget ( which is same as F)
  1956. varArgs[3] = hasOverridingNewTarget ? newTarget :
  1957. isCtorSuperCall ? args[0] : newTarget;
  1958. }
  1959. else
  1960. {
  1961. varArgs[2] = args[0];
  1962. varArgs[3] = argList;
  1963. }
  1964. Var trapResult = nullptr;
  1965. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  1966. {
  1967. trapResult = callMethod->CallFunction(arguments);
  1968. }
  1969. END_SAFE_REENTRANT_CALL
  1970. if (args.Info.Flags & CallFlags_New)
  1971. {
  1972. if (!Js::JavascriptOperators::IsObject(trapResult))
  1973. {
  1974. JavascriptError::ThrowTypeError(scriptContext, JSERR_InconsistentTrapResult, _u("construct"));
  1975. }
  1976. }
  1977. return trapResult;
  1978. }
  1979. JavascriptArray* JavascriptProxy::PropertyKeysTrap(KeysTrapKind keysTrapKind, ScriptContext* requestContext)
  1980. {
  1981. PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
  1982. // Reject implicit call
  1983. ThreadContext* threadContext = requestContext->GetThreadContext();
  1984. if (threadContext->IsDisableImplicitCall())
  1985. {
  1986. threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
  1987. return nullptr;
  1988. }
  1989. //1. Let handler be the value of the[[ProxyHandler]] internal slot of O.
  1990. RecyclableObject *handlerObj = this->MarshalHandler(requestContext);
  1991. //2. If handler is null, throw a TypeError exception.
  1992. //3. Assert: Type(handler) is Object.
  1993. if (handlerObj == nullptr)
  1994. {
  1995. // the proxy has been revoked; TypeError.
  1996. if (!threadContext->RecordImplicitException())
  1997. return nullptr;
  1998. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("ownKeys"));
  1999. }
  2000. AssertMsg(JavascriptOperators::IsObject(handlerObj), "Handler should be object.");
  2001. //4. Let target be the value of the[[ProxyTarget]] internal slot of O.
  2002. RecyclableObject *targetObj = this->MarshalTarget(requestContext);
  2003. //5. Let trap be GetMethod(handler, "ownKeys").
  2004. //6. ReturnIfAbrupt(trap).
  2005. //7. If trap is undefined, then
  2006. // a. Return target.[[OwnPropertyKeys]]().
  2007. JavascriptFunction* ownKeysMethod = GetMethodHelper(PropertyIds::ownKeys, requestContext);
  2008. Assert(!GetScriptContext()->IsHeapEnumInProgress());
  2009. JavascriptArray *targetKeys;
  2010. if (nullptr == ownKeysMethod)
  2011. {
  2012. switch (keysTrapKind)
  2013. {
  2014. case GetOwnPropertyNamesKind:
  2015. targetKeys = JavascriptOperators::GetOwnPropertyNames(targetObj, requestContext);
  2016. break;
  2017. case GetOwnPropertySymbolKind:
  2018. targetKeys = JavascriptOperators::GetOwnPropertySymbols(targetObj, requestContext);
  2019. break;
  2020. case KeysKind:
  2021. targetKeys = JavascriptOperators::GetOwnPropertyKeys(targetObj, requestContext);
  2022. break;
  2023. default:
  2024. AssertMsg(false, "Invalid KeysTrapKind.");
  2025. return requestContext->GetLibrary()->CreateArray(0);
  2026. }
  2027. return targetKeys;
  2028. }
  2029. //8. Let trapResultArray be Call(trap, handler, <<target>>).
  2030. //9. Let trapResult be CreateListFromArrayLike(trapResultArray, <<String, Symbol>>).
  2031. //10. ReturnIfAbrupt(trapResult).
  2032. //11. Let extensibleTarget be IsExtensible(target).
  2033. //12. ReturnIfAbrupt(extensibleTarget).
  2034. //13. Let targetKeys be target.[[OwnPropertyKeys]]().
  2035. //14. ReturnIfAbrupt(targetKeys).
  2036. Var ownKeysResult = threadContext->ExecuteImplicitCall(ownKeysMethod, ImplicitCall_Accessor, [=]()->Js::Var
  2037. {
  2038. return CALL_FUNCTION(threadContext, ownKeysMethod, CallInfo(CallFlags_Value, 2), handlerObj, targetObj);
  2039. });
  2040. if (!JavascriptOperators::IsObject(ownKeysResult))
  2041. {
  2042. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("ownKeys"));
  2043. }
  2044. RecyclableObject* trapResultArray = RecyclableObject::FromVar(ownKeysResult);
  2045. BOOL isTargetExtensible = targetObj->IsExtensible();
  2046. targetKeys = JavascriptOperators::GetOwnPropertyKeys(targetObj, requestContext);
  2047. //15. Assert: targetKeys is a List containing only String and Symbol values.
  2048. //16. Let targetConfigurableKeys be an empty List.
  2049. //17. Let targetNonconfigurableKeys be an empty List.
  2050. //18. Repeat, for each element key of targetKeys,
  2051. // a.Let desc be target.[[GetOwnProperty]](key).
  2052. // b.ReturnIfAbrupt(desc).
  2053. // c.If desc is not undefined and desc.[[Configurable]] is false, then
  2054. // i.Append key as an element of targetNonconfigurableKeys.
  2055. // d.Else,
  2056. // i.Append key as an element of targetConfigurableKeys.
  2057. //19. If extensibleTarget is true and targetNonconfigurableKeys is empty, then
  2058. // a. Return trapResult.
  2059. //20. Let uncheckedResultKeys be a new List which is a copy of trapResult.
  2060. //21. Repeat, for each key that is an element of targetNonconfigurableKeys,
  2061. // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception.
  2062. // b. Remove key from uncheckedResultKeys
  2063. //22. If extensibleTarget is true, return trapResult.
  2064. /*
  2065. To avoid creating targetConfigurableKeys, targetNonconfigurableKeys and uncheckedResultKeys list in above steps,
  2066. use below algorithm to accomplish same behavior
  2067. // Track if there are any properties that are present in target but not present in trap result
  2068. for(var i = 0; i < trapResult.length; i++)
  2069. {
  2070. PropertyId propId = GetPropertyId(trapResult[i]);
  2071. if(propId != NoProperty) { targetToTrapResultMap[propId] = 1; }
  2072. else { isTrapResultMissingFromTargetKeys = true; }
  2073. }
  2074. isConfigurableKeyMissingFromTrapResult = false;
  2075. isNonconfigurableKeyMissingFromTrapResult = false;
  2076. for(var i = 0; i < targetKeys.length; i++)
  2077. {
  2078. PropertyId propId = GetPropertyId(targetKeys[i]);
  2079. Var desc = GetPropertyDescriptor(propId);
  2080. if(targetToTrapResultMap[propId]) {
  2081. delete targetToTrapResultMap[propId];
  2082. isMissingFromTrapResult = false;
  2083. } else {
  2084. isMissingFromTrapResult = true;
  2085. }
  2086. if(desc->IsConfigurable()) {
  2087. if(isMissingFromTrapResult) {
  2088. isConfigurableKeyMissingFromTrapResult = true;
  2089. }
  2090. } else {
  2091. isAnyNonconfigurableKeyPresent = true
  2092. if(isMissingFromTrapResult) {
  2093. isNonconfigurableKeyMissingFromTrapResult = true;
  2094. }
  2095. }
  2096. }
  2097. // 19.
  2098. if(isExtensible && !isAnyNonconfigurableKeyPresent) { return trapResult; }
  2099. // 21.
  2100. if(isNonconfigurableKeyMissingFromTrapResult) { throw TypeError; }
  2101. // 22.
  2102. if(isExtensible) { return trapResult; }
  2103. // 23.
  2104. if(isConfigurableKeyMissingFromTrapResult) { throw TypeError; }
  2105. // 24.
  2106. if(!targetToTrapResultMap.Empty()) { throw TypeError; }
  2107. return trapResult;
  2108. */
  2109. JavascriptArray* trapResult = requestContext->GetLibrary()->CreateArray(0);
  2110. bool isConfigurableKeyMissingFromTrapResult = false;
  2111. bool isNonconfigurableKeyMissingFromTrapResult = false;
  2112. bool isKeyMissingFromTrapResult = false;
  2113. bool isKeyMissingFromTargetResult = false;
  2114. bool isAnyNonconfigurableKeyPresent = false;
  2115. Var element;
  2116. PropertyId propertyId;
  2117. const PropertyRecord* propertyRecord = nullptr;
  2118. BEGIN_TEMP_ALLOCATOR(tempAllocator, requestContext, _u("Runtime"))
  2119. {
  2120. // Dictionary containing intersection of keys present in targetKeys and trapResult
  2121. Var lenValue = JavascriptOperators::OP_GetLength(trapResultArray, requestContext);
  2122. uint32 len = (uint32)JavascriptConversion::ToLength(lenValue, requestContext);
  2123. JsUtil::BaseDictionary<Js::PropertyId, bool, ArenaAllocator> targetToTrapResultMap(tempAllocator, len);
  2124. // Trap result to return.
  2125. // Note : This will not necessarily have all elements present in trapResultArray. E.g. If trap was called from GetOwnPropertySymbols()
  2126. // trapResult will only contain symbol elements from trapResultArray.
  2127. switch (keysTrapKind)
  2128. {
  2129. case GetOwnPropertyNamesKind:
  2130. GetOwnPropertyKeysHelper(requestContext, trapResultArray, len, trapResult, targetToTrapResultMap,
  2131. [&](const PropertyRecord *propertyRecord)->bool
  2132. {
  2133. return !propertyRecord->IsSymbol();
  2134. });
  2135. break;
  2136. case GetOwnPropertySymbolKind:
  2137. GetOwnPropertyKeysHelper(requestContext, trapResultArray, len, trapResult, targetToTrapResultMap,
  2138. [&](const PropertyRecord *propertyRecord)->bool
  2139. {
  2140. return propertyRecord->IsSymbol();
  2141. });
  2142. break;
  2143. case KeysKind:
  2144. GetOwnPropertyKeysHelper(requestContext, trapResultArray, len, trapResult, targetToTrapResultMap,
  2145. [&](const PropertyRecord *propertyRecord)->bool
  2146. {
  2147. return true;
  2148. });
  2149. break;
  2150. }
  2151. for (uint32 i = 0; i < targetKeys->GetLength(); i++)
  2152. {
  2153. element = targetKeys->DirectGetItem(i);
  2154. 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)");
  2155. JavascriptConversion::ToPropertyKey(element, requestContext, &propertyRecord, nullptr);
  2156. propertyId = propertyRecord->GetPropertyId();
  2157. if (propertyId == Constants::NoProperty)
  2158. continue;
  2159. // If not present in intersection means either the property is not present in targetKeys or
  2160. // we have already visited the property in targetKeys
  2161. if (targetToTrapResultMap.ContainsKey(propertyId))
  2162. {
  2163. isKeyMissingFromTrapResult = false;
  2164. targetToTrapResultMap.Remove(propertyId);
  2165. }
  2166. else
  2167. {
  2168. isKeyMissingFromTrapResult = true;
  2169. }
  2170. PropertyDescriptor targetKeyPropertyDescriptor;
  2171. if (Js::JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetKeyPropertyDescriptor) && !targetKeyPropertyDescriptor.IsConfigurable())
  2172. {
  2173. isAnyNonconfigurableKeyPresent = true;
  2174. if (isKeyMissingFromTrapResult)
  2175. {
  2176. isNonconfigurableKeyMissingFromTrapResult = true;
  2177. }
  2178. }
  2179. else
  2180. {
  2181. if (isKeyMissingFromTrapResult)
  2182. {
  2183. isConfigurableKeyMissingFromTrapResult = true;
  2184. }
  2185. }
  2186. }
  2187. // Keys that were not found in targetKeys will continue to remain in the map
  2188. isKeyMissingFromTargetResult = targetToTrapResultMap.Count() != 0;
  2189. }
  2190. END_TEMP_ALLOCATOR(tempAllocator, requestContext)
  2191. // 19.
  2192. if (isTargetExtensible && !isAnyNonconfigurableKeyPresent)
  2193. {
  2194. return trapResult;
  2195. }
  2196. // 21.
  2197. if (isNonconfigurableKeyMissingFromTrapResult)
  2198. {
  2199. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("ownKeys"));
  2200. }
  2201. // 22.
  2202. if (isTargetExtensible)
  2203. {
  2204. return trapResult;
  2205. }
  2206. // 23.
  2207. if (isConfigurableKeyMissingFromTrapResult)
  2208. {
  2209. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("ownKeys"));
  2210. }
  2211. // 24.
  2212. if (isKeyMissingFromTargetResult)
  2213. {
  2214. JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("ownKeys"));
  2215. }
  2216. return trapResult;
  2217. }
  2218. #if ENABLE_TTD
  2219. void JavascriptProxy::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
  2220. {
  2221. if(this->handler != nullptr)
  2222. {
  2223. extractor->MarkVisitVar(this->handler);
  2224. }
  2225. if(this->target != nullptr)
  2226. {
  2227. extractor->MarkVisitVar(this->target);
  2228. }
  2229. }
  2230. TTD::NSSnapObjects::SnapObjectType JavascriptProxy::GetSnapTag_TTD() const
  2231. {
  2232. return TTD::NSSnapObjects::SnapObjectType::SnapProxyObject;
  2233. }
  2234. void JavascriptProxy::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  2235. {
  2236. TTD::NSSnapObjects::SnapProxyInfo* spi = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapProxyInfo>();
  2237. const uint32 reserveSize = 2;
  2238. uint32 depOnCount = 0;
  2239. TTD_PTR_ID* depOnArray = alloc.SlabReserveArraySpace<TTD_PTR_ID>(reserveSize);
  2240. spi->HandlerId = TTD_INVALID_PTR_ID;
  2241. if(this->handler != nullptr)
  2242. {
  2243. spi->HandlerId = TTD_CONVERT_VAR_TO_PTR_ID(this->handler);
  2244. if(TTD::JsSupport::IsVarComplexKind(this->handler))
  2245. {
  2246. depOnArray[depOnCount] = TTD_CONVERT_VAR_TO_PTR_ID(this->handler);
  2247. depOnCount++;
  2248. }
  2249. }
  2250. spi->TargetId = TTD_INVALID_PTR_ID;
  2251. if(this->target != nullptr)
  2252. {
  2253. spi->TargetId = TTD_CONVERT_VAR_TO_PTR_ID(this->target);
  2254. if(TTD::JsSupport::IsVarComplexKind(this->handler))
  2255. {
  2256. depOnArray[depOnCount] = TTD_CONVERT_VAR_TO_PTR_ID(this->target);
  2257. depOnCount++;
  2258. }
  2259. }
  2260. if(depOnCount == 0)
  2261. {
  2262. alloc.SlabAbortArraySpace<TTD_PTR_ID>(reserveSize);
  2263. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapProxyInfo*, TTD::NSSnapObjects::SnapObjectType::SnapProxyObject>(objData, spi);
  2264. }
  2265. else
  2266. {
  2267. alloc.SlabCommitArraySpace<TTD_PTR_ID>(depOnCount, reserveSize);
  2268. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapProxyInfo*, TTD::NSSnapObjects::SnapObjectType::SnapProxyObject>(objData, spi, alloc, depOnCount, depOnArray);
  2269. }
  2270. }
  2271. #endif
  2272. }