Skip to content
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

Disable scope hoisting when 'this' points to an export #9291

Merged
merged 24 commits into from
Oct 11, 2023

Conversation

adelchan07
Copy link
Contributor

@adelchan07 adelchan07 commented Oct 4, 2023

↪️ Pull Request

Add logic to disable scope hoisting if the "this" key word is pointing to an export module. Existing behavior only de-optimizes when encountering the "export" key word

Resolves issue #7866

💻 Examples

🚨 Test instructions

cargo test

✔️ PR Todo

  • Added/updated unit tests for this change
  • Filled out test instructions (In case there aren't any unit tests)
  • Included links to related issues/PRs

achan3 added 2 commits October 4, 2023 16:57
Co-authored-by: Eric Eldredge <lettertwo@gmail.com>
Co-authored-by: Brian Tedder <btedder@atlassian.com>
@parcel-benchmark
Copy link

parcel-benchmark commented Oct 4, 2023

Benchmark Results

Kitchen Sink ✅

Timings

Description Time Difference
Cold 2.14s +47.00ms
Cached 397.00ms +18.00ms

Cold Bundles

Bundle Size Difference Time Difference
dist/legacy/parcel.7cdb0fad.webp 102.94kb +0.00b 402.00ms +42.00ms ⚠️
dist/legacy/parcel.7cdb0fad.webp 102.94kb +0.00b 413.00ms +51.00ms ⚠️
dist/modern/parcel.7cdb0fad.webp 102.94kb +0.00b 412.00ms +51.00ms ⚠️
dist/legacy/index.ff03421b.js 1.48kb +0.00b 578.00ms +47.00ms ⚠️
dist/legacy/index.e9bb1616.js 1.06kb +0.00b 578.00ms +48.00ms ⚠️
dist/modern/index.4a29d309.js 921.00b +0.00b 577.00ms +47.00ms ⚠️
dist/legacy/index.html 826.00b +0.00b 612.00ms +43.00ms ⚠️
dist/modern/index.html 749.00b +0.00b 612.00ms +44.00ms ⚠️
dist/legacy/index.b8ae99ba.css 94.00b +0.00b 414.00ms +40.00ms ⚠️
dist/modern/index.31cedca9.css 94.00b +0.00b 413.00ms +40.00ms ⚠️

Cached Bundles

Bundle Size Difference Time Difference
dist/legacy/parcel.7cdb0fad.webp 102.94kb +0.00b 433.00ms +52.00ms ⚠️
dist/legacy/parcel.7cdb0fad.webp 102.94kb +0.00b 437.00ms +54.00ms ⚠️
dist/modern/parcel.7cdb0fad.webp 102.94kb +0.00b 437.00ms +55.00ms ⚠️
dist/legacy/index.ff03421b.js 1.48kb +0.00b 624.00ms +69.00ms ⚠️
dist/legacy/index.e9bb1616.js 1.06kb +0.00b 624.00ms +69.00ms ⚠️
dist/modern/index.4a29d309.js 921.00b +0.00b 624.00ms +70.00ms ⚠️
dist/legacy/index.html 826.00b +0.00b 659.00ms +66.00ms ⚠️
dist/modern/index.html 749.00b +0.00b 659.00ms +67.00ms ⚠️
dist/legacy/index.b8ae99ba.css 94.00b +0.00b 455.00ms +62.00ms ⚠️
dist/modern/index.31cedca9.css 94.00b +0.00b 455.00ms +62.00ms ⚠️

React HackerNews ✅

Timings

Description Time Difference
Cold 5.63s -186.00ms
Cached 555.00ms -13.00ms

Cold Bundles

Bundle Size Difference Time Difference
dist/index.js 459.11kb +0.00b 1.35s -169.00ms 🚀

Cached Bundles

Bundle Size Difference Time Difference
dist/index.js 459.11kb +0.00b 1.38s -148.00ms 🚀
dist/PermalinkedComment.e9dc4a75.js 3.92kb +0.00b 448.00ms -76.00ms 🚀
dist/UserProfile.8945a243.js 1.38kb +0.00b 448.00ms -75.00ms 🚀
dist/NotFound.8b44a81d.js 269.00b +0.00b 447.00ms -77.00ms 🚀

AtlasKit Editor ✅

Timings

Description Time Difference
Cold 49.73s -1.58s
Cached 3.01s -78.00ms

Cold Bundles

Bundle Size Difference Time Difference
dist/index.62f8ad91.js 3.78mb -362.00b 🚀 23.73s -1.44s 🚀
dist/refractor.3e0cc31b.js 598.96kb +0.00b 16.06s -1.26s 🚀
dist/media-viewer.38e3999a.js 536.13kb +0.00b 16.06s -1.26s 🚀
dist/popup.a77286c1.js 321.45kb +0.00b 16.06s -1.26s 🚀
dist/ConfigPanelFieldsLoader.182d39bc.js 303.43kb +0.00b 11.48s -871.00ms 🚀
dist/EmojiPickerComponent.4a196252.js 188.61kb +0.00b 16.06s -1.25s 🚀
dist/card.d06de810.js 138.91kb +0.00b 11.47s -871.00ms 🚀
dist/ConfigPanelFieldsLoader.28b428a5.js 82.73kb +0.00b 16.06s -1.25s 🚀
dist/esm.34897092.js 62.95kb +0.00b 16.06s -1.26s 🚀
dist/ElementBrowser.e8f01080.js 61.94kb +0.00b 11.48s -866.00ms 🚀
dist/archive.c374f622.js 59.90kb +0.00b 16.06s -1.26s 🚀
dist/esm.bfca2115.js 59.30kb +0.00b 11.48s -864.00ms 🚀
dist/component-lazy.51d1dec9.js 58.94kb +0.00b 8.62s +761.00ms ⚠️
dist/esm.5e913efb.js 39.11kb +0.00b 16.06s -1.26s 🚀
dist/smartMediaEditor.efa59853.js 21.68kb +0.00b 16.06s -1.26s 🚀
dist/esm.aee9cbf1.js 20.43kb +0.00b 16.06s -1.26s 🚀
dist/ConfigPanelFieldsLoader.2b7c03be.js 15.74kb +0.00b 11.48s -872.00ms 🚀
dist/ui.8c117104.js 14.48kb +0.00b 11.48s -870.00ms 🚀
dist/ConfigPanelFieldsLoader.5dfde67d.js 13.63kb +0.00b 11.48s -872.00ms 🚀
dist/dropzone.77a8e729.js 13.40kb +0.00b 16.06s -1.26s 🚀
dist/pdfRenderer.01deafa1.js 12.04kb -146.00b 🚀 11.47s -4.31s 🚀
dist/dropzone.1c15cdc1.js 11.48kb +0.00b 16.06s -1.26s 🚀
dist/Toolbar.4d256e97.js 9.36kb +0.00b 16.06s -1.26s 🚀
dist/clipboard.400013a2.js 7.92kb +0.00b 16.06s -1.26s 🚀
dist/mobile-upload.3baad8e4.js 7.79kb +0.00b 11.48s -870.00ms 🚀
dist/mobile-upload.7a892a37.js 7.79kb +0.00b 11.48s -870.00ms 🚀
dist/mobile-upload.2102debb.js 7.79kb +0.00b 16.06s -1.26s 🚀
dist/index.runtime.e32c1d2d.js 7.29kb +0.00b 16.06s -1.29s 🚀
dist/browser.0009c8b4.js 7.19kb +0.00b 16.06s -1.26s 🚀
dist/index.b16227d6.css 4.08kb +0.00b 16.08s -1.28s 🚀
dist/media-viewer-analytics-error-boundary.60bdaa4c.js 3.18kb +0.00b 16.06s -1.26s 🚀
dist/media-picker-analytics-error-boundary.c493f011.js 3.18kb +0.00b 16.06s -1.26s 🚀
dist/media-card-analytics-error-boundary.74e0c7f9.js 3.18kb +0.00b 16.06s -1.26s 🚀
dist/ru.0cf3f40e.js 2.81kb +0.00b 11.48s -871.00ms 🚀
dist/uk.282f23b1.js 2.76kb +0.00b 11.48s -871.00ms 🚀
dist/codeViewerRenderer.51140ec8.js 2.61kb +0.00b 11.47s -4.31s 🚀
dist/th.137e1013.js 2.60kb +0.00b 11.48s -870.00ms 🚀
dist/vi.b46097db.js 2.09kb +0.00b 11.48s -872.00ms 🚀
dist/tr.c85d90a9.js 2.03kb +0.00b 11.48s -871.00ms 🚀
dist/sv.1c06c95c.js 1.98kb +0.00b 11.48s -871.00ms 🚀
dist/zh_TW.b7c55aa6.js 1.86kb +0.00b 11.48s -872.00ms 🚀
dist/zh.b01fe721.js 1.84kb +0.00b 11.48s -872.00ms 🚀
dist/workerHasher.540c9790.js 1.56kb +0.00b 11.48s -866.00ms 🚀
dist/workerHasher.c840c607.js 1.56kb +0.00b 11.48s -871.00ms 🚀
dist/workerHasher.730f3766.js 1.56kb +0.00b 16.06s -1.26s 🚀
dist/workerHasher.9b1fcdbf.js 1.56kb +0.00b 16.06s -1.26s 🚀
dist/workerHasher.02b63a21.js 1.56kb +0.00b 16.06s -1.26s 🚀
dist/sk.4be9c93f.js 656.00b +0.00b 11.48s -871.00ms 🚀
dist/simpleHasher.c14e20b4.js 589.00b +0.00b 11.48s -870.00ms 🚀
dist/simpleHasher.23db7a52.js 589.00b +0.00b 11.48s -871.00ms 🚀
dist/simpleHasher.eefc98b4.js 589.00b +0.00b 16.06s -1.26s 🚀
dist/simpleHasher.47b9c809.js 589.00b +0.00b 16.06s -1.26s 🚀
dist/simpleHasher.cadc19c6.js 589.00b +0.00b 16.06s -1.26s 🚀
dist/ro.8d5b380a.js 482.00b +0.00b 11.48s +2.45s ⚠️
dist/index.html 248.00b +0.00b 8.66s -8.73s 🚀

Cached Bundles

Bundle Size Difference Time Difference
dist/index.62f8ad91.js 3.78mb -362.00b 🚀 24.39s +186.00ms
dist/refractor.3e0cc31b.js 598.96kb +0.00b 17.81s +976.00ms ⚠️
dist/media-viewer.38e3999a.js 536.13kb +0.00b 17.82s +979.00ms ⚠️
dist/popup.a77286c1.js 321.45kb +0.00b 17.81s +976.00ms ⚠️
dist/EmojiPickerComponent.4a196252.js 188.61kb +0.00b 17.80s +958.00ms ⚠️
dist/ConfigPanelFieldsLoader.28b428a5.js 82.73kb +0.00b 17.80s +958.00ms ⚠️
dist/esm.34897092.js 62.95kb +0.00b 17.81s +977.00ms ⚠️
dist/archive.c374f622.js 59.90kb +0.00b 17.82s +981.00ms ⚠️
dist/component-lazy.51d1dec9.js 58.94kb +0.00b 8.17s -737.00ms 🚀
dist/esm.5e913efb.js 39.11kb +0.00b 17.81s +977.00ms ⚠️
dist/smartMediaEditor.efa59853.js 21.68kb +0.00b 17.81s +985.00ms ⚠️
dist/esm.aee9cbf1.js 20.43kb +0.00b 17.81s +985.00ms ⚠️
dist/dropzone.77a8e729.js 13.40kb +0.00b 17.81s +977.00ms ⚠️
dist/pdfRenderer.01deafa1.js 12.04kb -146.00b 🚀 12.49s +308.00ms
dist/dropzone.1c15cdc1.js 11.48kb +0.00b 17.81s +977.00ms ⚠️
dist/Toolbar.4d256e97.js 9.36kb +0.00b 17.81s +985.00ms ⚠️
dist/clipboard.400013a2.js 7.92kb +0.00b 17.81s +977.00ms ⚠️
dist/mobile-upload.2102debb.js 7.79kb +0.00b 17.81s +983.00ms ⚠️
dist/index.runtime.e32c1d2d.js 7.29kb +0.00b 17.83s +943.00ms ⚠️
dist/browser.0009c8b4.js 7.19kb +0.00b 17.81s +977.00ms ⚠️
dist/media-viewer-analytics-error-boundary.60bdaa4c.js 3.18kb +0.00b 17.82s +981.00ms ⚠️
dist/media-picker-analytics-error-boundary.c493f011.js 3.18kb +0.00b 17.81s +977.00ms ⚠️
dist/media-card-analytics-error-boundary.74e0c7f9.js 3.18kb +0.00b 17.82s +981.00ms ⚠️
dist/workerHasher.730f3766.js 1.56kb +0.00b 17.81s +977.00ms ⚠️
dist/workerHasher.9b1fcdbf.js 1.56kb +0.00b 17.81s +977.00ms ⚠️
dist/workerHasher.02b63a21.js 1.56kb +0.00b 17.81s +983.00ms ⚠️
dist/heading4.be08fc9e.js 1.12kb +0.00b 8.17s -735.00ms 🚀
dist/simpleHasher.eefc98b4.js 589.00b +0.00b 17.81s +977.00ms ⚠️
dist/simpleHasher.47b9c809.js 589.00b +0.00b 17.81s +977.00ms ⚠️
dist/simpleHasher.cadc19c6.js 589.00b +0.00b 17.81s +983.00ms ⚠️

Three.js ✅

Timings

Description Time Difference
Cold 4.26s +118.00ms
Cached 432.00ms +10.00ms

Cold Bundles

No bundle changes detected.

Cached Bundles

No bundle changes detected.

Click here to view a detailed benchmark overview.

@imbrian
Copy link
Contributor

imbrian commented Oct 4, 2023

This does wrap unnecessarily on...

class Foo {
  constructor() {
    this.a = 4
  }

  bar() {
    return this.baz()
  }

  baz() {
    return this.a
  }
}

exports.baz = new Foo()
exports.a = 2

Should we only track the this usage outside of classes?

@adelchan07 adelchan07 requested review from mischnic, devongovett, lettertwo and imbrian and removed request for mischnic October 6, 2023 16:19
adelchan07 and others added 2 commits October 6, 2023 12:53
Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
@mischnic
Copy link
Member

mischnic commented Oct 6, 2023

I assume you ran into some specific dependency that uses this pattern just like #7866 did?

Because this only fixes a very specific set of cases and not for example

exports.foo = function () {
	let x = this;
	x.bar();
    // or
	let key = "bar";
	this[key]();
};

exports.bar = function () {
	return 2;
};

But on the other hand, just testing for this in itself isn't pratical either

@adelchan07
Copy link
Contributor Author

I assume you ran into some specific dependency that uses this pattern just like #7866 did?

Because this only fixes a very specific set of cases and not for example

exports.foo = function () {
	let x = this;
	x.bar();
    // or
	let key = "bar";
	this[key]();
};

exports.bar = function () {
	return 2;
};

But on the other hand, just testing for this in itself isn't pratical either

the PR was originally created to specifically address the issue with retry (as mentioned in #7866).

Would you prefer handling all cases related to 'this' pointing to an export in a single PR, or could we merge the fix for retry and handle other cases in another PR. From our side, would prefer to merge this PR in time for the next release to unblock another product that were integrating with Parcel.

If we decide to split into different PRs, will update the PR description accordingly @imbrian @mischnic

@mischnic
Copy link
Member

mischnic commented Oct 7, 2023

handle other cases in another PR

Ideally we'd have to do as little of this inexact this-detection as possible. So if you don't have to, don't 😅

The only concern I have with this solution is that you can get false positives, e.g. with

exports.foo = function(){...};

// Usage: `new SomeClass()`
exports.SomeClass = function SomeClass(){
    this.foo = 123;
};

But even if that happens, it's not that bad to have one more wrapped module.

@devongovett
Copy link
Member

Theoretically we could also only wrap if the actual namespace is imported (eg import *) and not when only named imports are used. Not sure if it makes a big difference.

packages/transformers/js/core/src/collect.rs Outdated Show resolved Hide resolved
} else if !self.in_class {
if let MemberProp::Ident(prop) = &node.prop {
self.this_exprs.insert(id!(prop), node.clone());
}
Copy link
Member

Choose a reason for hiding this comment

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

Do we only want to handle this in exported functions? If you have a non-exported function that uses this we shouldn't need to wrap. You could track which functions use this instead of each property access, and then later check if any of the exported functions used this. Might be more accurate than just looking for property names?

Copy link
Contributor Author

@adelchan07 adelchan07 Oct 10, 2023

Choose a reason for hiding this comment

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

If we are tracking functions that use this, there would have to be some sort of struct that maps the Ident for the original exported function to every re-assignment of the function to another variable. Discussed this with @imbrian and we were thinking that the overhead from wrapping every time a this is encountered seems better than having to keep a (possibly very large) struct of every possible alias referring back to a function that calls this.

@adelchan07
Copy link
Contributor Author

FYI: changes don't seem to significantly affect build time (44.27s for this PR, 43.59s for v2) and is a actually faster build for the Atlassian repo we are working to integrate Parcel into @devongovett @mischnic @imbrian

@adelchan07 adelchan07 merged commit 75c1921 into v2 Oct 11, 2023
16 checks passed
@adelchan07 adelchan07 deleted the disable-scope-hoisting-for-all-exports branch October 11, 2023 18:19
adelchan07 added a commit that referenced this pull request Oct 18, 2023
* recognize 'this' as a potential keyword linking to an export

* Add co-authors.
Co-authored-by: Eric Eldredge <lettertwo@gmail.com>
Co-authored-by: Brian Tedder <btedder@atlassian.com>

* update BailoutReason text, fix test name

* rename "thises" to "this_exprs"

* remove print statements

* cleanup

* don't wrap when `this` collides in class

* move bailouts outside of tracing

* add integration test

* cleanup integration test

* update test: move to commonjs and change search

* update naming

* Update packages/core/integration-tests/test/scope-hoisting.js

Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>

* move fixture for test into commonjs folder

* update link for THisInExport bailout reason

* remove duplicate memberProp check

* Revert "remove duplicate memberProp check"

This reverts commit a017082.

* store Ident, Span instead of entire node

* move instead of clone self.this_exprs

* Cleanup

---------

Co-authored-by: achan3 <achan3@atlassian.com>
Co-authored-by: Brian Tedder <btedder@atlassian.com>
Co-authored-by: Brian Tedder <6571474+imbrian@users.noreply.github.com>
Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants