-
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
Fix configurability of function.length and boundFunction.length #5405
Conversation
@@ -28,7 +28,7 @@ var tests = [ | |||
get: function () { } | |||
}); | |||
} | |||
assert.doesNotThrow(function () { g('length') }, "assertion failure on defineProperty 'length' with getter after sealing a function object"); | |||
assert.throws(function () { g('length') }, TypeError, "Cannot redefine non-configurable property 'length'"); | |||
assert.throws(function () { g('arguments') }, TypeError, "Cannot redefine non-configurable property 'arguments'"); |
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 test was testing incorrect behavior see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal
3d2b284
to
3b2262e
Compare
I really like the idea of having Function.length participate in the type system in these cases. Since you ask -- yes, there is an existing model for lazily instantiating properties of function objects. See DeferredTypeHandler::EnsureObjectReady, DeferredTypeHandler::ConvertFunction, and JavascriptLibrary::InitializeFunction. These make use of the SimpleTypeHandler's in the JavascriptLibrary code. Someone can correct me, but it looks like we've defined function object type handlers for length only, length-and-name, and prototype-and-length variants, but not prototype-length-and-name. I wonder if we added length to the handler variant for user functions and removed length from the set of "special properties" if we'd get what we want. |
len = len - this->count; | ||
len = max(len, 0); | ||
|
||
SetPropertyWithAttributes(PropertyIds::length, TaggedInt::ToVarUnchecked(len), PropertyConfigurable, nullptr, PropertyOperation_None, SideEffects_None); | ||
} | ||
|
||
BoundFunction::BoundFunction(RecyclableObject* targetFunction, Var boundThis, Var* args, uint argsCount, DynamicType * type) |
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.
Haven't worked out the purpose of this alternate constructor - unsure if I need to add length handling in here as well.
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 suspect that this is dead code now; certainly chakracore doesn't reference it at all, and I also can't find any references in chakrafull either.
Looked at this more, got a few further problems/questions:
(Sorry about the questions, I thought this would be easier to solve than it was) |
For question 1, it does seem inconsistent. I'd be in favor of eliminating the special handling of Function.length, instantiating it in JavascriptLibrary::InitializeFunction (if present) the way we do name and prototype, and setting up the shared SimpleTypeHandler's to support it. For question 2, sorry, I don't know. I can't tell where that length value is supposed to be coming from just by eyeballing the test, but it certainly seems as though the baseline may be wrong. |
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 don't have a ton to say on the content here, but anything to fix all of that special casing for PropertyIds::length
in Function and BoundFunction code is 👍 from me.
{ | ||
return true; | ||
function->SetPropertyWithAttributes(PropertyIds::length, TaggedInt::ToVarUnchecked(function->GetFunctionProxy()->EnsureDeserialized()->GetReportedInParamsCount() - 1), PropertyConfigurable, nullptr, PropertyOperation_None, SideEffects_None); | ||
if ((useAnonymous || scriptFunction->GetFunctionProxy()->EnsureDeserialized()->GetIsStaticNameFunction())) |
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.
Cache scriptFunction->GetFunctionProxy()->EnsureDeserialized()
somewhere? The compiler may be smart enough to cache this, but it would make these two lines look a bit nicer.
I think I have this right now, thank you for the help @pleath This could obviously do with a fairly thorough review nervous that some of the types are used in numerous places and there's a chance I missed something. This change now:
|
} | ||
else | ||
{ | ||
typeHandler->ConvertFunction(function, useAnonymous ? javascriptLibrary->anonymousFunctionWithPrototypeTypeHandler : javascriptLibrary->functionWithPrototypeTypeHandler); | ||
typeHandler->ConvertFunction(function, useAnonymous ? javascriptLibrary->anonymousFunctionWithPrototypeTypeHandler : | ||
useLengthType ? javascriptLibrary->functionWithPrototypeAndLengthTypeHandler : javascriptLibrary->functionWithPrototypeTypeHandler); |
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.
nit: double ternaries can be pretty hard to read.
|
||
// Reduce length number of bound args | ||
len = len - this->count; | ||
len = max(len, 0); |
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.
Could you add a test for this case (where the target function is over-bound)?
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.
Done. Though i’ve also realised that i’m missing tests for anonymous functions which take a slightly different path. I’ll add those later and also try and refactor the testfile to remove some of the duplication.
Thanks, @rhuanjl, I’ll give this a close look this week. |
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.
Got to love the deletion of special cases. Thanks.
Thanks for the reviews @jackhorton @pleath Does this need anything else or is it good to merge? If it's good to merge please can one of you pick up? |
Is there anything more I need to do on this for it to be merged? |
sorry @rhuanjl , I will run this through internal tests and get it merged tonight. sorry for the delay! |
This fails a number of ChakraFull tests, almost certainly all of which are baseline updates but I will report back with more tomorrow. |
I suppose this is the problem with making a low-is level change as an external contributor when I can't look at the output of internal tests. Question will be whether they were testing incorrect behaviour e.g. expecting deleting a length to fail OR whether I've inadvertently changed something else e.g. an early version of this change (which I didn't submit) would have added a length property to all host created functions (which currently don't have a length by default). |
It looks like the majority of the tests are because the enumeration order of the |
The enumeration order seems to be implementation defined; at least node-v8 and node-chakracore report them in different orders. |
Historically, we’ve caused problems for people when we change enumeration order. People do take dependencies on implementation-defined behavior. So I’d prefer to preserve the original order if we can do that and still get the benefit of this change. |
I'll take a look at where I've changed enumeration order and see if I can change it back (as obviously it's not changed in a way that effects any CC tests at the moment). @jackhorton if you could give me any details on the other failing test and what javascriptlibrary.cpp / javascriptfunction.cpp functions it calls I'll see if I can work out what could have changed. |
@jackhorton is there any update on this/anything I can do to help get it merged? is the failing test something that could be ported to chakracore so I could investigate it? |
@@ -924,7 +978,7 @@ namespace Js | |||
|
|||
DynamicType * JavascriptLibrary::CreateDeferredPrototypeFunctionType(JavascriptMethod entrypoint) | |||
{ | |||
return CreateDeferredPrototypeFunctionTypeNoProfileThunk(this->inDispatchProfileMode ? ProfileEntryThunk : entrypoint); | |||
return CreateDeferredPrototypeFunctionTypeNoProfileThunk(this->inDispatchProfileMode ? ProfileEntryThunk : entrypoint, true); |
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.
FINALLY got some real time to look at this, and this line was the problem for the chakrafull jsrt test. Do you remember what the goal was here? It doesn't seem related to the rest of the PR, and reverting the line causes the tests to pass (don't feel the need to make the change yourself if its ok to revert, I will push my own that's easier to sync with the chakrafull test changes I have already made).
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 change should be reverted.
It's not doing what it was meant to and isn't needed anyway - I had intended to give this type a length (which is not needed) and this is wrong for doing that anyway would have needed to be false, true
rather than just true
.
Looking back over this my only remaining concern is function naming - this PR largely makes the default for DeferredFunction types be that they have a length property. This one is left without a length property and naming wise nothing making clear that it doesn't have one.
Wondering if I should rename some of the other CreateDeferred...FunctionType methods to make it explicit when types do have lengths. That could be a follow up PR if that would be simpler for syncing with your chakra full changes?
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 think we can leave giving these lengths turned off for now and test it when development ramps back up for the next windows release (since these include projected and custom external functions, we would probably have to make the change based on the results of our chakrafull test suite, so that will be on us). As for the name, it may be confusing, but since the goal is to have lengths be enabled by default everywhere in the future, I think its fine to be in this state for now (especially since it was already inconsistent before, so its not a regression). @pleath sounds like a plan?
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 fine with reverting the one line, pushing what we have, and adjusting the names as a separate PR, if that's what you're suggesting.
a1a3401
to
3a32201
Compare
Looking back over this, wondering if to avoid any undetected problems I/we should revert the changes I’ve made to other types and add new types instead? (Pressed close by mistake) |
@jackhorton based on pleath’s comment do you want to land this as is? I’ll follow up with another PR to fix up names of some methods and also correct remaining errors with generator and async function length. |
Sure. Re-running internal tests now |
I think this is based on a commit with a corresponding chakrafull commit that is flaky, I am seeing random test issues. I am going to force-push this branch to be rebased on top of latest master which should help. |
Merge pull request #5573 from rhuanjl:flat This PR implements the stage 3 proposal Array methods: Array.prototype.flat and Array.prototype.flatMap as JsBuiltIns. Draft specification text is here: https://tc39.github.io/proposal-flatMap/ Notes: 1. 3 test262 failures: - length is not configurable (for both methods), this is due to ScriptFunction lengths in CC all not being configurable and should be fixed if #5405 is merged - the case using null as this fails -- EDIT: FIXED 2. I can't find a feasible way to test the length is too big error - not sure if CC can actually handle arrays that big anyway so maybe not worth implementing that case? 3. Should probably have a few more CI tests Fixes #5543
3a32201
to
148ffba
Compare
@rhuanjl One more issue came up from the latest round of internal tests that I missed before -- the ChakraFull debug system was adding its own version of the length property to the list of properties to show in the debugger, which caused it to show up twice. I updated the branch to be on top of the latest master with the debugger fix -- Id like for @kfarnung and/or @akroshg to look at the change in DiagObjectModel.cpp to make sure its valid. |
@@ -2563,11 +2562,6 @@ namespace Js | |||
InsertItem(originalObject, object, propertyId, isConst, isUnscoped, &pMethodsGroupWalker); | |||
} | |||
} | |||
else if ((jsFunction->IsScriptFunction() && !jsFunction->GetFunctionProxy()->IsJsBuiltInCode()) || jsFunction->IsBoundFunction()) | |||
{ | |||
// Adding special property length for the ScriptFunction, like it is done in JavascriptFunction::GetSpecialNonEnumerablePropertyName |
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 function it references doesn't seem to exist anymore, unless my code search tools are blatantly lying to me.
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 is hard to remember - but I think only one I can find is JavascriptRegExpConstructor::GetSpecialNonEnumerablePropertyName
so that means this may have been removed.
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.
Last commit LGTM.
Remove all special casing of length from BoundFunction.cpp and JavascriptFunction.cpp As a result of above configurability of function.length and boundFunction.length
…ateDeferredPrototypeFunctionType
148ffba
to
cbada71
Compare
…ndFunction.length Merge pull request #5405 from rhuanjl:boundFunction Fix: #4122 This PR addresses several issues with the length property of JavascriptFunctions and BoundFunctions. Length should be configurable (previously worked for JavascriptFunctions but not BoundFunctions) Deleting length should succeed (previously failed for both) Deleting length should not throw in strict mode (previously failed for both) value after deletion = 0 (previously failed for both) using defineProperty to redefine length should work (previously failed silently for both)
@rhuanjl sorry for all of the delays here, and thanks again for this! Its been a long-standing compat issue for us. |
Very cool. This is the kind of improvement that's available to us now with our new type-sharing model. |
…urable length Merge pull request #5610 from rhuanjl:generatorLength Follow up to #5405 Generator and async functions should also have configurable lengths - currently they don't. Specific test262 tests: https://github.com/tc39/test262/blob/master/test/built-ins/AsyncFunction/instance-length.js https://github.com/tc39/test262/blob/master/test/built-ins/GeneratorFunction/instance-length.js Additionally the second commit restores some consistency to function type handler names (I'd unintentionally made these inconsistent in #5405) it also prevents some internal anonymous functions from unnecessarily being given a length property - unintentional side effect of #5405. fixes: #5419 CC: @pleath @jackhorton
Fix: #4122
This PR addresses several issues with the length property of JavascriptFunctions and BoundFunctions.
Length should be configurable (previously worked for JavascriptFunctions but not BoundFunctions)
Deleting length should succeed (previously failed for both)
Deleting length should not throw in strict mode (previously failed for both)
value after deletion = 0 (previously failed for both)
using defineProperty to redefine length should work (previously failed silently for both)