8000 Fix: improve object rest handling in array pattern by JLHwung · Pull Request #17281 · babel/babel · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Fix: improve object rest handling in array pattern #17281

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

Merged
merged 6 commits into from
May 5, 2025

Conversation

JLHwung
Copy link
Contributor
@JLHwung JLHwung commented Apr 30, 2025
Q                       A
Fixed Issues? Fixes #17271, fixes #17272
Patch: Bug Fix? Yes
Major: Breaking Change?
Minor: New Feature?
Tests Added + Pass? Yes
Documentation PR Link
Any Dependency Changes?
License MIT

This PR contains two refactors and the array pattern fix. On https://gist.github.com/nicolo-ribaudo/f8ac7916f89450f2ead77d99855b2098, I plan to implement the object pattern split as we have already done in the destructuring-private transform, which is a more ambitious goal. This PR, as a temporary fix, should unblock the discard binding transform PR.

@JLHwung JLHwung added PR: Bug Fix 🐛 A type of pull request used for our changelog categories Spec: Object Rest/Spread labels Apr 30, 2025
@@ -0,0 +1,7 @@
let a = 0, result, y;

for ({ [a++]: result, ...y} of [["0", "1"]]) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bug is fixed by the second refactoring commit.

@babel-bot
Copy link
Collaborator
babel-bot commented Apr 30, 2025

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/59270

@JLHwung JLHwung mentioned this pull request May 1, 2025
5 tasks
Copy link
Member
@nicolo-ribaudo nicolo-ribaudo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. I wonder whether eventually we should implement transform for object rest, destructuring, and private destructuring all in a single package, similarly to how we do for class features and regexps.

@JLHwung JLHwung requested a review from liuxingbaoyu May 5, 2025 12:46
Copy link
Member
@liuxingbaoyu liuxingbaoyu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On gist.github.com/nicolo-ribaudo/f8ac7916f89450f2ead77d99855b2098, I plan to implement the object pattern split as we have already done in the destructuring-private transform, which is a more ambitious goal.

It would be nice if we could do it without using iterators. :)

@JLHwung JLHwung merged commit 83c72d0 into babel:main May 5, 2025
57 checks passed
@JLHwung JLHwung deleted the fix-object-rest branch May 5, 2025 18:09
@JLHwung
Copy link
Contributor Author
JLHwung commented May 5, 2025

It would be nice if we could do it without using iterators. :)

Could you elaborate why you would like to avoid iterators?

@liuxingbaoyu
Copy link
Member

Symbol.iterator is supported in chrome 43, and rest parameters are supported in chrome 47, which means that it will be difficult for users to use it without polyfill.
The benefit is better support for getter behavior, which may be rarely used.

@JLHwung
Copy link
Contributor Author
JLHwung commented May 6, 2025

Symbol.iterator is supported in chrome 43, and rest parameters are supported in chrome 47, which means that it will be difficult for users to use it without polyfill. The benefit is better support for getter behavior, which may be rarely used.

Thanks. We are on the same page, what I was meant for the "object pattern split" is that we handle the object rest within an object pattern as same as how we currently handle private destructuring. For example, the current private destructuring transforms

var { a, b: { i: x, #p: p }, c } = rhs

to

var { a } = rhs,
      _m = rhs.b,
      { i: x } = _m,
      p = _m.#p,
      { c } = rhs;

where the object pattern is split into three parts: part before the private access, at the private access and after the private.

Now if we take a look at the current object rest transforms,

var { a, b: { i: x, ...p }, c } = rhs

is currently transformed to

const _excluded = ["i"];
var { a, b: { i: x }, c } = rhs,
      p = _objectWithoutProperties(rhs.b, _excluded);

where b has been accessed twice, and the c is accessed before the ...p access. Now thinking ...p as if it were #p: p, if we reuse the private-destructuring object pattern split, then it should be transformed to

const _excluded = ["i"];
var { a } = rhs,
      _m = rhs.b,
      { i: x } = _m,
      p = _objectWithoutProperties(_m, _excluded),
      { c } = rhs;

where the b access is memoised and the c access is now placed correctly after p is evaluated, and the footprint increase is acceptable.

The private-destructuring assumes the iterable is pure: it does not affect how other nested patterns are evaluated. If this assumption is not held, then we will have to implement the step iterator, which, like you said, will be difficult for users without polyfill. Since we don't implement the step iterator for private-destructuring, I don't plan do so for the object-rest either.

@liuxingbaoyu
Copy link
Member

Thanks for the detailed explanation! That sounds good.
We can keep the original as pureGetters assumption.

The private-destructuring assumes the iterable is pure: it does not affect how other nested patterns are evaluated. If this assumption is not held, then we will have to implement the step iterator, which, like you said, will be difficult for users without polyfill. Since we don't implement the step iterator for private-destructuring, I don't plan do so for the object-rest either.

Sorry I didn't understand, can you provide an example that is not pure?

@JLHwung
Copy link
Contributor Author
JLHwung commented May 6, 2025

See

case "ArrayPattern": {
// todo: the transform here assumes that any expression within
// the array pattern, when evaluated, do not interfere with the iterable
// in RHS. Otherwise we have to pause the iterable and interleave
// the expressions.
// See also https://gist.github.com/nicolo-ribaudo/f8ac7916f89450f2ead77d99855b2098
// and ordering/array-pattern-side-effect-iterable test
const leftElements = left.elements;
const leftElementsAfterIndex = leftElements.splice(index);
const { elements, transformed } = buildAssignmentsFromPatternList(
leftElementsAfterIndex,
scope,
isAssignment,
);
leftElements.push(...elements);
yield { left, right: cloneNode(right) };
// for elements after `index`, push them to stack so we can process them later
for (let i = transformed.length - 1; i > 0; i--) {
// skipping array holes
if (transformed[i] !== null) {
stack.push(transformed[i]);
}
}
({ left, right } = transformed[0]);
break;

and https://github.com/babel/babel/blob/main/packages/babel-plugin-proposal-destructuring-private/test/fixtures/ordering/.array-pattern-side-effect-iterable/exec.js

This test has been disabled because we don't support it: A proper support would require the aforementioned step iterator.

@JLHwung
Copy link
Contributor Author
JLHwung commented May 6, 2025

We can keep the original as pureGetters assumption.

Even with the pureGetters assumption enabled, we still need to fix the incorrect evaluation ordering. The current object-rest transform can not handle this case:

const { a: { ...r }, b = r } = rhs

because b was incorrectly evaluated before r.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: Bug Fix 🐛 A type of pull request used for our changelog categories Spec: Object Rest/Spread
Projects
None yet
4 participants
0