@@ -2486,132 +2486,164 @@ namespace Js
24862486 return SetProperty_Internal<false >(instance, instance, true , propertyId, newValue, info, requestContext, propertyOperationFlags);
24872487 }
24882488
2489- template <bool unscopables>
2490- BOOL JavascriptOperators::SetProperty_Internal (Var receiver, RecyclableObject* object, const bool isRoot, PropertyId propertyId, Var newValue, PropertyValueInfo * info, ScriptContext* requestContext, PropertyOperationFlags propertyOperationFlags)
2491- {
2492- if (receiver)
2489+ // Returns true if a result was written.
2490+ bool JavascriptOperators::SetAccessorOrNonWritableProperty (
2491+ Var receiver,
2492+ RecyclableObject* object,
2493+ PropertyId propertyId,
2494+ Var newValue,
2495+ PropertyValueInfo * info,
2496+ ScriptContext* requestContext,
2497+ PropertyOperationFlags propertyOperationFlags,
2498+ bool isRoot,
2499+ bool allowUndecInConsoleScope,
2500+ BOOL *result)
2501+ {
2502+ *result = FALSE ;
2503+ Var setterValueOrProxy = nullptr ;
2504+ DescriptorFlags flags = None;
2505+ if ((isRoot && JavascriptOperators::CheckPrototypesForAccessorOrNonWritableRootProperty (object, propertyId, &setterValueOrProxy, &flags, info, requestContext)) ||
2506+ (!isRoot && JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty (object, propertyId, &setterValueOrProxy, &flags, info, requestContext)))
24932507 {
2494- Assert (!TaggedNumber::Is (receiver));
2495- Var setterValueOrProxy = nullptr ;
2496- DescriptorFlags flags = None;
2497- if ((isRoot && JavascriptOperators::CheckPrototypesForAccessorOrNonWritableRootProperty (object, propertyId, &setterValueOrProxy, &flags, info, requestContext)) ||
2498- (!isRoot && JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty (object, propertyId, &setterValueOrProxy, &flags, info, requestContext)))
2508+ if ((flags & Accessor) == Accessor)
24992509 {
2500- if ((flags & Accessor) == Accessor)
2510+ if (JavascriptError::ThrowIfStrictModeUndefinedSetter (propertyOperationFlags, setterValueOrProxy, requestContext) ||
2511+ JavascriptError::ThrowIfNotExtensibleUndefinedSetter (propertyOperationFlags, setterValueOrProxy, requestContext))
2512+ {
2513+ *result = TRUE ;
2514+ return true ;
2515+ }
2516+ if (setterValueOrProxy)
25012517 {
2502- if (JavascriptError::ThrowIfStrictModeUndefinedSetter (propertyOperationFlags, setterValueOrProxy, requestContext) ||
2503- JavascriptError::ThrowIfNotExtensibleUndefinedSetter (propertyOperationFlags, setterValueOrProxy, requestContext))
2518+ RecyclableObject* func = RecyclableObject::FromVar (setterValueOrProxy);
2519+ Assert (!info || info->GetFlags () == InlineCacheSetterFlag || info->GetPropertyIndex () == Constants::NoSlot);
2520+
2521+ if (WithScopeObject::Is (receiver))
25042522 {
2505- return TRUE ;
2523+ receiver = ( RecyclableObject::FromVar (receiver))-> GetThisObjectOrUnWrap () ;
25062524 }
2507- if (setterValueOrProxy)
2525+ else
25082526 {
2509- RecyclableObject* func = RecyclableObject::FromVar (setterValueOrProxy);
2510- Assert (!info || info->GetFlags () == InlineCacheSetterFlag || info->GetPropertyIndex () == Constants::NoSlot);
2511-
2512- if (WithScopeObject::Is (receiver))
2513- {
2514- receiver = (RecyclableObject::FromVar (receiver))->GetThisObjectOrUnWrap ();
2515- }
2516- else
2517- {
2518- CacheOperators::CachePropertyWrite (RecyclableObject::FromVar (receiver), isRoot, object->GetType (), propertyId, info, requestContext);
2519- }
2527+ CacheOperators::CachePropertyWrite (RecyclableObject::FromVar (receiver), isRoot, object->GetType (), propertyId, info, requestContext);
2528+ }
25202529#ifdef ENABLE_MUTATION_BREAKPOINT
2521- if (MutationBreakpoint::IsFeatureEnabled (requestContext))
2522- {
2523- MutationBreakpoint::HandleSetProperty (requestContext, object, propertyId, newValue);
2524- }
2525- #endif
2526- JavascriptOperators::CallSetter (func, receiver, newValue, requestContext);
2530+ if (MutationBreakpoint::IsFeatureEnabled (requestContext))
2531+ {
2532+ MutationBreakpoint::HandleSetProperty (requestContext, object, propertyId, newValue);
25272533 }
2528- return TRUE ;
2534+ #endif
2535+ JavascriptOperators::CallSetter (func, receiver, newValue, requestContext);
25292536 }
2530- else if ((flags & Proxy) == Proxy)
2531- {
2532- Assert (JavascriptProxy::Is (setterValueOrProxy));
2533- JavascriptProxy* proxy = JavascriptProxy::FromVar (setterValueOrProxy);
2534- // 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
2535- // invalidate when target, handler, or handler prototype has changed. We don't have a way to achieve this yet.
2536- PropertyValueInfo::SetNoCache (info, proxy);
2537- PropertyValueInfo::DisablePrototypeCache (info, proxy); // We can't cache prototype property either
2537+ *result = TRUE ;
2538+ return true ;
2539+ }
2540+ else if ((flags & Proxy) == Proxy)
2541+ {
2542+ Assert (JavascriptProxy::Is (setterValueOrProxy));
2543+ JavascriptProxy* proxy = JavascriptProxy::FromVar (setterValueOrProxy);
2544+ // 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
2545+ // invalidate when target, handler, or handler prototype has changed. We don't have a way to achieve this yet.
2546+ PropertyValueInfo::SetNoCache (info, proxy);
2547+ PropertyValueInfo::DisablePrototypeCache (info, proxy); // We can't cache prototype property either
25382548
2539- return proxy->SetPropertyTrap (receiver, JavascriptProxy::SetPropertyTrapKind::SetPropertyKind, propertyId, newValue, requestContext);
2540- }
2541- else
2549+ *result = proxy->SetPropertyTrap (receiver, JavascriptProxy::SetPropertyTrapKind::SetPropertyKind, propertyId, newValue, requestContext);
2550+ return true ;
2551+ }
2552+ else
2553+ {
2554+ Assert ((flags & Data) == Data && (flags & Writable) == None);
2555+ if (!allowUndecInConsoleScope)
25422556 {
2543- Assert ((flags & Data) == Data && (flags & Writable) == None);
25442557 if (flags & Const)
25452558 {
25462559 JavascriptError::ThrowTypeError (requestContext, ERRAssignmentToConst);
25472560 }
25482561
25492562 JavascriptError::ThrowCantAssign (propertyOperationFlags, requestContext, propertyId);
25502563 JavascriptError::ThrowCantAssignIfStrictMode (propertyOperationFlags, requestContext);
2551- return FALSE ;
2564+
2565+ *result = FALSE ;
2566+ return true ;
25522567 }
25532568 }
2554- else if (!JavascriptOperators::IsObject (receiver))
2555- {
2556- JavascriptError::ThrowCantAssignIfStrictMode (propertyOperationFlags, requestContext);
2557- return FALSE ;
2558- }
2569+ }
2570+ return false ;
2571+ }
2572+
2573+ template <bool unscopables>
2574+ BOOL JavascriptOperators::SetProperty_Internal (Var receiver, RecyclableObject* object, const bool isRoot, PropertyId propertyId, Var newValue, PropertyValueInfo * info, ScriptContext* requestContext, PropertyOperationFlags propertyOperationFlags)
2575+ {
2576+ if (receiver == nullptr )
2577+ {
2578+ return FALSE ;
2579+ }
2580+
2581+ Assert (!TaggedNumber::Is (receiver));
2582+ BOOL setAccessorResult = FALSE ;
2583+ if (SetAccessorOrNonWritableProperty (receiver, object, propertyId, newValue, info, requestContext, propertyOperationFlags, isRoot, false , &setAccessorResult))
2584+ {
2585+ return setAccessorResult;
2586+ }
2587+ else if (!JavascriptOperators::IsObject (receiver))
2588+ {
2589+ JavascriptError::ThrowCantAssignIfStrictMode (propertyOperationFlags, requestContext);
2590+ return FALSE ;
2591+ }
25592592
25602593#ifdef ENABLE_MUTATION_BREAKPOINT
2561- // Break on mutation if needed
2562- bool doNotUpdateCacheForMbp = MutationBreakpoint::IsFeatureEnabled (requestContext) ?
2563- MutationBreakpoint::HandleSetProperty (requestContext, object, propertyId, newValue) : false ;
2594+ // Break on mutation if needed
2595+ bool doNotUpdateCacheForMbp = MutationBreakpoint::IsFeatureEnabled (requestContext) ?
2596+ MutationBreakpoint::HandleSetProperty (requestContext, object, propertyId, newValue) : false ;
25642597#endif
25652598
2566- // Get the original type before setting the property
2567- Type *typeWithoutProperty = object->GetType ();
2568- BOOL didSetProperty = false ;
2569- if (isRoot)
2570- {
2571- AssertMsg (JavascriptOperators::GetTypeId (receiver) == TypeIds_GlobalObject
2572- || JavascriptOperators::GetTypeId (receiver) == TypeIds_ModuleRoot,
2573- " Root must be a global object!" );
2599+ // Get the original type before setting the property
2600+ Type *typeWithoutProperty = object->GetType ();
2601+ BOOL didSetProperty = false ;
2602+ if (isRoot)
2603+ {
2604+ AssertMsg (JavascriptOperators::GetTypeId (receiver) == TypeIds_GlobalObject
2605+ || JavascriptOperators::GetTypeId (receiver) == TypeIds_ModuleRoot,
2606+ " Root must be a global object!" );
25742607
2575- RootObjectBase* rootObject = static_cast <RootObjectBase*>(receiver);
2576- didSetProperty = rootObject->SetRootProperty (propertyId, newValue, propertyOperationFlags, info);
2577- }
2578- else
2608+ RootObjectBase* rootObject = static_cast <RootObjectBase*>(receiver);
2609+ didSetProperty = rootObject->SetRootProperty (propertyId, newValue, propertyOperationFlags, info);
2610+ }
2611+ else
2612+ {
2613+ RecyclableObject* instanceObject = RecyclableObject::FromVar (receiver);
2614+ while (!JavascriptOperators::IsNull (instanceObject))
25792615 {
2580- RecyclableObject* instanceObject = RecyclableObject::FromVar (receiver);
2581- while (!JavascriptOperators::IsNull (instanceObject))
2616+ if (unscopables && JavascriptOperators::IsPropertyUnscopable (instanceObject, propertyId))
25822617 {
2583- if (unscopables && JavascriptOperators::IsPropertyUnscopable (instanceObject, propertyId))
2618+ break ;
2619+ }
2620+ else
2621+ {
2622+ didSetProperty = instanceObject->SetProperty (propertyId, newValue, propertyOperationFlags, info);
2623+ if (didSetProperty || !unscopables)
25842624 {
25852625 break ;
25862626 }
2587- else
2588- {
2589- didSetProperty = instanceObject->SetProperty (propertyId, newValue, propertyOperationFlags, info);
2590- if (didSetProperty || !unscopables)
2591- {
2592- break ;
2593- }
2594- }
2595- instanceObject = JavascriptOperators::GetPrototypeNoTrap (instanceObject);
25962627 }
2628+ instanceObject = JavascriptOperators::GetPrototypeNoTrap (instanceObject);
25972629 }
2630+ }
25982631
2599- if (didSetProperty)
2600- {
2601- bool updateCache = true ;
2632+ if (didSetProperty)
2633+ {
2634+ bool updateCache = true ;
26022635#ifdef ENABLE_MUTATION_BREAKPOINT
2603- updateCache = updateCache && !doNotUpdateCacheForMbp;
2636+ updateCache = updateCache && !doNotUpdateCacheForMbp;
26042637#endif
26052638
2606- if (updateCache)
2639+ if (updateCache)
2640+ {
2641+ if (!JavascriptProxy::Is (receiver))
26072642 {
2608- if (!JavascriptProxy::Is (receiver))
2609- {
2610- CacheOperators::CachePropertyWrite (RecyclableObject::FromVar (receiver), isRoot, typeWithoutProperty, propertyId, info, requestContext);
2611- }
2643+ CacheOperators::CachePropertyWrite (RecyclableObject::FromVar (receiver), isRoot, typeWithoutProperty, propertyId, info, requestContext);
26122644 }
2613- return TRUE ;
26142645 }
2646+ return TRUE ;
26152647 }
26162648
26172649 return FALSE ;
@@ -2939,50 +2971,10 @@ namespace Js
29392971 // is true, this must be a normal property.
29402972 // TODO: merge OP_HasProperty and GetSetter in one pass if there is perf problem. In fastDOM we have quite
29412973 // a lot of setters so separating the two might be actually faster.
2942- Var setterValueOrProxy = nullptr ;
2943- DescriptorFlags flags = None;
2944- if (JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty (object, propertyId, &setterValueOrProxy, &flags, &info, scriptContext))
2974+ BOOL setAccessorResult = FALSE ;
2975+ if (SetAccessorOrNonWritableProperty (object, object, propertyId, newValue, &info, scriptContext, propertyOperationFlags, false , allowUndecInConsoleScope, &setAccessorResult))
29452976 {
2946- if ((flags & Accessor) == Accessor)
2947- {
2948- if (setterValueOrProxy)
2949- {
2950- JavascriptFunction* func = (JavascriptFunction*)setterValueOrProxy;
2951- Assert (info.GetFlags () == InlineCacheSetterFlag || info.GetPropertyIndex () == Constants::NoSlot);
2952- CacheOperators::CachePropertyWrite (object, false , type, propertyId, &info, scriptContext);
2953- JavascriptOperators::CallSetter (func, object, newValue, scriptContext);
2954- }
2955-
2956- Assert (!isLexicalThisSlotSymbol);
2957- return ;
2958- }
2959- else if ((flags & Proxy) == Proxy)
2960- {
2961- Assert (JavascriptProxy::Is (setterValueOrProxy));
2962- JavascriptProxy* proxy = JavascriptProxy::FromVar (setterValueOrProxy);
2963- auto fn = [&](RecyclableObject* target) -> BOOL {
2964- return JavascriptOperators::SetProperty (object, target, propertyId, newValue, scriptContext, propertyOperationFlags);
2965- };
2966- // 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
2967- // invalidate when target, handler, or handler prototype has changed. We don't have a way to achieve this yet.
2968- PropertyValueInfo::SetNoCache (&info, proxy);
2969- PropertyValueInfo::DisablePrototypeCache (&info, proxy); // We can't cache prototype property either
2970- proxy->SetPropertyTrap (object, JavascriptProxy::SetPropertyTrapKind::SetPropertyKind, propertyId, newValue, scriptContext);
2971- }
2972- else
2973- {
2974- Assert ((flags & Data) == Data && (flags & Writable) == None);
2975- if (!allowUndecInConsoleScope)
2976- {
2977- if (flags & Const)
2978- {
2979- JavascriptError::ThrowTypeError (scriptContext, ERRAssignmentToConst);
2980- }
2981-
2982- Assert (!isLexicalThisSlotSymbol);
2983- return ;
2984- }
2985- }
2977+ return ;
29862978 }
29872979 else if (!JavascriptOperators::IsObject (object))
29882980 {
0 commit comments