Skip to content

[reactive-element] Always wrap user accessors#4146

Merged
justinfagnani merged 14 commits into3.0from
3.0-hybrid-decorators-3
Aug 30, 2023
Merged

[reactive-element] Always wrap user accessors#4146
justinfagnani merged 14 commits into3.0from
3.0-hybrid-decorators-3

Conversation

@justinfagnani
Copy link
Collaborator

Part of #4144

This makes all reactive properties - static properties block, experimental decorators, and standard decorators - wrap user written accessors and call this.requestUpdate() from the generated setter.

@changeset-bot
Copy link

changeset-bot bot commented Aug 26, 2023

🦋 Changeset detected

Latest commit: 3e85612

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@lit/reactive-element Major
lit-element Major
lit Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Contributor

github-actions bot commented Aug 26, 2023

📊 Tachometer Benchmark Results

Summary

nop-update

  • this-change, tip-of-tree, previous-release: faster ✔ 3% - 15% (0.68ms - 3.43ms)
    this-change vs tip-of-tree

render

  • this-change: 74.62ms - 78.86ms
  • this-change, tip-of-tree, previous-release: unsure 🔍 -3% - +8% (-0.95ms - +2.29ms)
    this-change vs tip-of-tree
  • this-change, tip-of-tree, previous-release: unsure 🔍 -2% - +3% (-0.89ms - +1.50ms)
    this-change vs tip-of-tree
  • this-change, tip-of-tree, previous-release: unsure 🔍 -2% - +2% (-0.72ms - +1.01ms)
    this-change vs tip-of-tree

update

  • this-change: 841.08ms - 855.19ms
  • this-change, tip-of-tree, previous-release: unsure 🔍 -6% - +4% (-4.91ms - +3.53ms)
    this-change vs tip-of-tree
  • this-change, tip-of-tree, previous-release: unsure 🔍 -1% - +1% (-1.76ms - +1.49ms)
    this-change vs tip-of-tree
  • this-change, tip-of-tree, previous-release: unsure 🔍 -1% - +0% (-11.27ms - +1.06ms)
    this-change vs tip-of-tree

update-reflect

  • this-change: 798.91ms - 808.22ms
  • this-change, tip-of-tree, previous-release: unsure 🔍 -1% - +0% (-5.84ms - +3.94ms)
    this-change vs tip-of-tree

Results

this-change

render

VersionAvg timevs
74.62ms - 78.86ms-

update

VersionAvg timevs
841.08ms - 855.19ms-

update-reflect

VersionAvg timevs
798.91ms - 808.22ms-
this-change, tip-of-tree, previous-release

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
28.87ms - 31.27ms-unsure 🔍
-3% - +8%
-0.95ms - +2.29ms
unsure 🔍
-6% - +4%
-1.68ms - +1.34ms
tip-of-tree
tip-of-tree
28.31ms - 30.48msunsure 🔍
-8% - +3%
-2.29ms - +0.95ms
-unsure 🔍
-7% - +2%
-2.27ms - +0.58ms
previous-release
previous-release
29.32ms - 31.16msunsure 🔍
-4% - +6%
-1.34ms - +1.68ms
unsure 🔍
-2% - +8%
-0.58ms - +2.27ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
80.72ms - 84.97ms-unsure 🔍
-6% - +4%
-4.91ms - +3.53ms
unsure 🔍
-3% - +6%
-2.54ms - +4.93ms
tip-of-tree
tip-of-tree
79.89ms - 87.18msunsure 🔍
-4% - +6%
-3.53ms - +4.91ms
-unsure 🔍
-4% - +8%
-2.88ms - +6.65ms
previous-release
previous-release
78.57ms - 84.72msunsure 🔍
-6% - +3%
-4.93ms - +2.54ms
unsure 🔍
-8% - +3%
-6.65ms - +2.88ms
-

nop-update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
18.99ms - 20.74ms-faster ✔
3% - 15%
0.68ms - 3.43ms
unsure 🔍
-11% - +4%
-2.23ms - +0.86ms
tip-of-tree
tip-of-tree
20.86ms - 22.97msslower ❌
3% - 18%
0.68ms - 3.43ms
-unsure 🔍
-2% - +15%
-0.29ms - +3.03ms
previous-release
previous-release
19.27ms - 21.82msunsure 🔍
-4% - +11%
-0.86ms - +2.23ms
unsure 🔍
-14% - +1%
-3.03ms - +0.29ms
-
this-change, tip-of-tree, previous-release

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
51.78ms - 53.21ms-unsure 🔍
-2% - +3%
-0.89ms - +1.50ms
unsure 🔍
-1% - +3%
-0.38ms - +1.62ms
tip-of-tree
tip-of-tree
51.23ms - 53.15msunsure 🔍
-3% - +2%
-1.50ms - +0.89ms
-unsure 🔍
-2% - +3%
-0.87ms - +1.50ms
previous-release
previous-release
51.18ms - 52.57msunsure 🔍
-3% - +1%
-1.62ms - +0.38ms
unsure 🔍
-3% - +2%
-1.50ms - +0.87ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
124.28ms - 126.05ms-unsure 🔍
-1% - +1%
-1.76ms - +1.49ms
unsure 🔍
-1% - +1%
-1.35ms - +1.18ms
tip-of-tree
tip-of-tree
123.94ms - 126.67msunsure 🔍
-1% - +1%
-1.49ms - +1.76ms
-unsure 🔍
-1% - +1%
-1.59ms - +1.69ms
previous-release
previous-release
124.34ms - 126.16msunsure 🔍
-1% - +1%
-1.18ms - +1.35ms
unsure 🔍
-1% - +1%
-1.69ms - +1.59ms
-
this-change, tip-of-tree, previous-release

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
45.29ms - 46.58ms-unsure 🔍
-2% - +2%
-0.72ms - +1.01ms
unsure 🔍
-2% - +2%
-1.00ms - +0.79ms
tip-of-tree
tip-of-tree
45.22ms - 46.36msunsure 🔍
-2% - +2%
-1.01ms - +0.72ms
-unsure 🔍
-2% - +1%
-1.09ms - +0.59ms
previous-release
previous-release
45.43ms - 46.66msunsure 🔍
-2% - +2%
-0.79ms - +1.00ms
unsure 🔍
-1% - +2%
-0.59ms - +1.09ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
869.31ms - 878.15ms-unsure 🔍
-1% - +0%
-11.27ms - +1.06ms
unsure 🔍
-1% - +0%
-10.36ms - +2.00ms
tip-of-tree
tip-of-tree
874.54ms - 883.13msunsure 🔍
-0% - +1%
-1.06ms - +11.27ms
-unsure 🔍
-1% - +1%
-5.17ms - +7.02ms
previous-release
previous-release
873.59ms - 882.23msunsure 🔍
-0% - +1%
-2.00ms - +10.36ms
unsure 🔍
-1% - +1%
-7.02ms - +5.17ms
-

update-reflect

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
860.84ms - 868.46ms-unsure 🔍
-1% - +0%
-5.84ms - +3.94ms
unsure 🔍
-1% - +0%
-7.36ms - +4.31ms
tip-of-tree
tip-of-tree
862.54ms - 868.67msunsure 🔍
-0% - +1%
-3.94ms - +5.84ms
-unsure 🔍
-1% - +1%
-5.95ms - +4.80ms
previous-release
previous-release
861.76ms - 870.59msunsure 🔍
-0% - +1%
-4.31ms - +7.36ms
unsure 🔍
-1% - +1%
-4.80ms - +5.95ms
-

tachometer-reporter-action v2 for Benchmarks

@github-actions
Copy link
Contributor

The size of lit-html.js and lit-core.min.js are as expected.

@justinfagnani justinfagnani force-pushed the 3.0-hybrid-decorators-3 branch from 69e2ac1 to 89e513e Compare August 26, 2023 15:54
@justinfagnani justinfagnani changed the title Always wrap user accessors [reactive-element] Always wrap user accessors Aug 28, 2023
_foo?: number;
updatedContent?: number;

@property({reflect: true, type: Number})
Copy link
Collaborator

Choose a reason for hiding this comment

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

How does this code behave with this change? Is it a breaking change?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Er, I should say before this change

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's not a breaking change with experimentalDecorators: true because those read the entire property descriptor off the class and it doesn't matter which is decorated.

Standard decorator usage requires that we decorate the setter though, because we're only passed the setter method.

I can test both in legacy tests.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added a test for decorating a getter in experimental decorators. I don't think we can detect which accessor was decorated, so I couldn't add a deprecation warning.

//
// In either case, update the size here and push a new commit to your PR.
const expectedLitCoreSize = 15023;
const expectedLitCoreSize = 15135;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Huh, bigger change than I'd have expected from this PR

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah... there's a bit of change here. I'm going to change the approach slightly, though I don't think it'll be fewer bytes

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

my new change made it worse: 15214 bytes, but I think aside from that it's a better implementation.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok, kept it more static and got the size down to 15115

Base automatically changed from 3.0-hybrid-decorators-2 to 3.0 August 29, 2023 18:19
// eslint-disable-next-line @typescript-eslint/no-explicit-any
get(): any {
return (this as {[key: string]: unknown})[key as string];
return get === undefined
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

actually... I want to change this so that the conditional is outside of the accessors and run only once

// user-defined accessors. Note that if the super has an accessor we will
// still overwrite it
if (!options.noAccessor && !this.prototype.hasOwnProperty(name)) {
if (!options.noAccessor) {
Copy link
Contributor

@AndrewJakubowicz AndrewJakubowicz Aug 29, 2023

Choose a reason for hiding this comment

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

For my own understanding, is this where the wrapping is happening? !this.prototype.hasOwnProperty(name) would previously only wrap class fields and skip accessors or user defined get/set.
Now explicit getters/setters and accessor keyword cases pass through this code path (in addition to the class fields).

@@ -728,21 +729,22 @@ export abstract class ReactiveElement
key: string | symbol,
Copy link
Contributor

@AndrewJakubowicz AndrewJakubowicz Aug 29, 2023

Choose a reason for hiding this comment

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

Does the comment on this method need to be updated? Specifically the part: If no descriptor is returned, the property will not become an accessor.

I think it needs to be updated to mention that it now wraps accessors.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That comment is still true because of the if (descriptor !== undefined) check in createProperty

Copy link
Contributor

@AndrewJakubowicz AndrewJakubowicz left a comment

Choose a reason for hiding this comment

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

Nice! The tests are really great as well!

// Check initial values
assert.equal(el._foo, undefined);
assert.equal(el.updatedContent, undefined);
assert.equal(el.updatedProperties!.get('foo'), undefined);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Here and in the main decorators tests: should we assert on el.updatedProperties!.has('foo') here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Since there's no initializer it'll be false, but I can assert on that instead

@justinfagnani justinfagnani merged commit 0f6878d into 3.0 Aug 30, 2023
@justinfagnani justinfagnani deleted the 3.0-hybrid-decorators-3 branch August 30, 2023 05:15
This was referenced Sep 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants