-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Array.copyWithin should throw when invoked on TypedArray with missing slots #4790
Conversation
@@ -4818,7 +4818,8 @@ namespace Js | |||
#endif | |||
} | |||
|
|||
if (result == FALSE && (propertyOperationFlags & PropertyOperation_StrictMode)) | |||
if (result == FALSE && | |||
(propertyOperationFlags & (PropertyOperation_StrictMode | PropertyOperation_ThrowOnDeleteIfNotConfig))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks reasonable to me, but it does make me wonder whether there's some other scenario somewhere that might directly call into someArray->DeleteItem
and expect that it throws when appropriate; a quick look around showed code like this: https://github.com/Microsoft/ChakraCore/blob/master/lib/Runtime/Library/JavascriptArray.cpp#L5649 which I think converts the false
return into throwing explicitly, but seems to expect the typed array will do something with the flag it is given rather than completely ignoring it. Do you know if there are any other potential scenarios? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
Not sure this is the right place for this check. When result == FALSE
, it might be for another reason other than not configurable
. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
result
looks like it always is returned by JavascriptOperators::DeleteProperty
. What other reason could it return false for? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks to me like result is only false in the non-configurable case, so this should work.
Like Jimmy mentioned, various other methods invite misuse because they accept a PropertyOperationFlags parameter but don't pay attention to PropertyOperation_ThrowOnDeleteIfNotConfig. It seems somewhat inconsistent; some methods like PathTypeHandlerBase::DeleteProperty do respect that flag, but others don't. I'm not sure if it's worth trying to track them all down at this point.
In reply to: 173333391 [](ancestors = 173333391)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After all, it seems that it's better to have this handling in TypedArrayBase::DeleteItem as we discussed before :)
In reply to: 173024703 [](ancestors = 173024703)
@dotnet-bot test Pre-Merge signing tool please #Resolved |
That's not part of the dotnet CI; I think a push will be required to trigger that. #Resolved |
@@ -9131,7 +9131,8 @@ namespace Js | |||
} | |||
else | |||
{ | |||
JS_REENTRANT(jsReentLock, obj->DeleteItem(toIndex, PropertyOperation_ThrowOnDeleteIfNotConfig)); | |||
JavascriptOperators::OP_DeleteElementI(obj, JavascriptNumber::ToVar(toIndex, scriptContext), scriptContext, PropertyOperation_ThrowOnDeleteIfNotConfig); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we still need to wrap this in JS_REENTRANT? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For more context, we explicitly mark any lines in here that are allowed to call back into script (it would be a bug if any line without such a marking could call script). This pattern is useful because we can assume that certain things don't change as long as we haven't called into script (for instance, an instance of JavascriptArray hasn't magically transformed itself into an instance of ES5Array). The object here could be a Proxy with a deleteProperty handler, in which case this deletion operation would call script.
In reply to: 173333445 [](ancestors = 173333445)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the explanations! I'm reverting this file but I might need to follow up offline to understand better about the Proxies and reentrancy :)
In reply to: 173337067 [](ancestors = 173337067,173333445)
@@ -4818,7 +4818,8 @@ namespace Js | |||
#endif | |||
} | |||
|
|||
if (result == FALSE && (propertyOperationFlags & PropertyOperation_StrictMode)) | |||
if (result == FALSE && | |||
(propertyOperationFlags & (PropertyOperation_StrictMode | PropertyOperation_ThrowOnDeleteIfNotConfig))) | |||
{ | |||
JavascriptError::ThrowCantDelete(propertyOperationFlags, scriptContext, GetPropertyDisplayNameForError(index, scriptContext)->GetString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't JavascriptError::ThrowCantDelete
already check propertyOperationFlags
? Can we write this as:
if (!result)
{
JavascriptError::ThrowCantDelete(propertyOperationFlags, scriptContext, GetPropertyDisplayNameForError(index, scriptContext)->GetString());
}
``` #Resolved
@@ -9131,7 +9131,8 @@ namespace Js | |||
} | |||
else | |||
{ | |||
JS_REENTRANT(jsReentLock, obj->DeleteItem(toIndex, PropertyOperation_ThrowOnDeleteIfNotConfig)); | |||
JavascriptOperators::OP_DeleteElementI(obj, JavascriptNumber::ToVar(toIndex, scriptContext), scriptContext, PropertyOperation_ThrowOnDeleteIfNotConfig); | |||
//JS_REENTRANT(jsReentLock, obj->DeleteItem(toIndex, PropertyOperation_ThrowOnDeleteIfNotConfig)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also nitpick, please delete this commented line. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm moving the throw into TypedArrayBase to be aligned with other DeleteProperty/DeleteItem handlers, so there will be no change here.
In reply to: 173349228 [](ancestors = 173349228)
@@ -694,7 +694,7 @@ namespace Js | |||
return false; | |||
} | |||
|
|||
bool JavascriptError::ThrowCantDelete(PropertyOperationFlags flags, ScriptContext* scriptContext, PCWSTR varName) | |||
bool JavascriptError::ThrowCantDeleteIfStrictModeOrNonconfigurable(PropertyOperationFlags flags, ScriptContext* scriptContext, PCWSTR varName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ThrowCantDeleteIfStrictModeOrNonconfigurable [](start = 26, length = 44)
Ugly name, but similar to "ThrowCantDeleteIfStrictMode". #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ThrowCantDeleteIfRequiredForNonConfigurableProperty
? Current name sounds like it would throw if the property was nonconfigurable (regardless of the operation flags), which isn't true (and in fact this method has no way to even know if the property was configurable, which must be checked by the caller).
In reply to: 173516752 [](ancestors = 173516752)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, on the topic of this method, I don't like how it forces the caller to do a possibly-expensive conversion to string before figuring out whether we need it. I'd rather take a Var and do GetPropertyDisplayNameForError(varName, scriptContext)->GetString()
only once we determine that we actually need to throw. Throwing is super expensive anyway, so any additional conversion cost there doesn't particularly matter. Anyhow, this wasn't introduced by your change, so feel free to leave as-is.
In reply to: 173520340 [](ancestors = 173520340,173516752)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- name: it throws either in strict mode or if non-configurable. Because this information can only be extracted from the flags, I think the name is more clear now (it doesn't give the impression that it guarantees to throw anymore).
- string vs var: the callers seem to differ in how they get hold of the string and I don't have a good grasp of details to assess whether var would make perf better for them.
In reply to: 173521221 [](ancestors = 173521221,173520340,173516752)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be fair to have an overload of this method which takes a Var
for varName
and then use GetPropertyDisplayNameForError
if we'll throw an error. Otherwise don't do the conversion. Could save some time in that case.
@@ -4818,11 +4818,6 @@ namespace Js | |||
#endif | |||
} | |||
|
|||
if (result == FALSE && (propertyOperationFlags & PropertyOperation_StrictMode)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To try and catch any errors in future, it might be worth to add an assert here along the lines of Assert(result || !(propertyOperationFlags & PropertyOperation_StrictMode)
; that way there is no overhead in release builds, but we should catch regressions in testing. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe result || !(propertyOperationFlags & (PropertyOperation_StrictMode | PropertyOperation_ThrowOnDeleteOrNotConfig))
, since the point of this change is updating the contract elsewhere to respect both flags
In reply to: 173516816 [](ancestors = 173516816)
@@ -851,6 +851,13 @@ namespace Js | |||
return PropertyQueryFlags::Property_NotFound_NoProto; | |||
} | |||
|
|||
BOOL TypedArrayBase::DeleteItem(uint32 index, Js::PropertyOperationFlags flags) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
flags [](start = 77, length = 5)
when running in strict mode, the flags will contain PropertyOperation_StrictMode
methods such as copyWithin would pass PropertyOperation_ThrowOnDeleteIfNotConfig #Resolved
@@ -912,7 +912,8 @@ namespace Js | |||
else if (!(descriptor->Attributes & PropertyConfigurable)) | |||
{ | |||
// Let/const properties do not have attributes and they cannot be deleted | |||
JavascriptError::ThrowCantDelete(propertyOperationFlags, scriptContext, propertyNameString->GetString()); | |||
JavascriptError::ThrowCantDeleteIfStrictModeOrNonconfigurable( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ThrowCantDeleteIfStrictModeOrNonconfigurable [](start = 33, length = 44)
I wonder whether this one (and a few other current callers of ThrowCantDeleteIfStrictModeOrNonconfigurable) should actually call ThrowCantDeleteIfStrictMode because I don't think the flags would be set here to PropertyOperation_ThrowOnDeleteIfNotConfig (but I don't know how to confirm this). #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems safer to call the one that respects both flags. If ThrowOnDeleteIfNotConfig is never set, then it would make no difference in behavior.
In reply to: 173517742 [](ancestors = 173517742)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, that it's safer. I was wondering about whether we could limit usage of ThrowCantDeleteIfStrictModeOrNonconfigurable (because I don't like this method :)).
In reply to: 173523027 [](ancestors = 173523027,173517742)
@@ -879,7 +879,8 @@ namespace Js | |||
} | |||
if (!(attr & ObjectSlotAttr_Configurable)) | |||
{ | |||
JavascriptError::ThrowCantDelete(PropertyOperation_None, scriptContext, scriptContext->GetPropertyName(propertyId)->GetBuffer()); | |||
JavascriptError::ThrowCantDeleteIfStrictModeOrNonconfigurable( | |||
flags, scriptContext, scriptContext->GetPropertyName(propertyId)->GetBuffer()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
flags [](start = 16, length = 5)
Fix for #4118 (to throw when deleting non-configurable properties in strict mode) #Resolved
<test> | ||
<default> | ||
<files>13.delete_sm.js</files> | ||
<baseline>13.delete_sm.baseline</baseline> | ||
</default> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This existing test passes "as is" so decided not to rewrite it :) #Resolved
Looks like this test has caused a test in |
I think the new behavior is more correct, so the test just needs to have its expectation updated I believe. #Resolved |
Yes, a different error message because. I've updated the test. In reply to: 371887110 [](ancestors = 371887110) |
I don't know why the signing tool didn't kick in on my PRs yesterday, but it seems to be doing so now. In reply to: 371616241 [](ancestors = 371616241) |
… slots Rename ThrowCantDelete and fix regression from chakra-core#4118 (delete non-config props in strict mode)
ceb0481
to
f219294
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Fixes #4356