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

feat: track decorator reform #1428

Merged
merged 7 commits into from
Aug 12, 2019
Merged

Conversation

jodarove
Copy link
Contributor

@jodarove jodarove commented Jul 23, 2019

Details

RFC for this change: salesforce/lwc-rfcs#4

The logic for this PR is as follows:

When the compiler compiles a class, it extracts any field that is not decorated with @api, @track or @wire, and pass the metadata through the registerDecorators call.

Given that in order to observe a field we need a vm, we need to register the fields of all classes and save it in the decoratorsMeta, we wait until we create the ComponentDefinition to know which classes will be used as components.

For every class field we will observe in a Component, we will create a getter and a setter in the prototype of the ComponentDefinition (and the class inheritance chain until BaseLightningElement).

No change is needed to the logic in the engine.

Does this PR introduce breaking changes?

  • 🚨 Yes, it does introduce breaking changes.

When modifying a reactive property (tracked) in the render method, it will cause rehydration during render.

This will trigger an engine invariant violation similar to the following: Error: Invariant Violation: [object:vm undefined (500)].render() method has side effects on the state of [object:vm undefined (500)].renderCount.

Upgrade path: Use the renderedCallback in such cases.

@jodarove jodarove added the work-in-progress Work in progress label Jul 23, 2019
@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: 165ad3b | Target commit: 239c569

lwc-engine-benchmark

table-append-1k metric base(165ad3b) target(239c569) trend
benchmark-table/append/1k duration 141.10 (±2.70 ms) 145.20 (±4.60 ms) +4.1ms (2.9%) 👎
table-clear-1k metric base(165ad3b) target(239c569) trend
benchmark-table/clear/1k duration 11.35 (±0.95 ms) 10.60 (±0.80 ms) -0.8ms (6.6%) 👍
table-create-10k metric base(165ad3b) target(239c569) trend
benchmark-table/create/10k duration 844.45 (±7.05 ms) 863.55 (±5.35 ms) +19.1ms (2.3%) 👎
table-create-1k metric base(165ad3b) target(239c569) trend
benchmark-table/create/1k duration 107.60 (±3.50 ms) 108.75 (±2.65 ms) +1.2ms (1.1%) 👌
table-update-10th-1k metric base(165ad3b) target(239c569) trend
benchmark-table/update-10th/1k duration 69.00 (±3.35 ms) 77.90 (±3.60 ms) +8.9ms (12.9%) 👎
tablecmp-append-1k metric base(165ad3b) target(239c569) trend
benchmark-table-component/append/1k duration 220.80 (±15.50 ms) 223.10 (±7.90 ms) +2.3ms (1.0%) 👌
tablecmp-clear-1k metric base(165ad3b) target(239c569) trend
benchmark-table-component/clear/1k duration 5.95 (±0.95 ms) 6.40 (±1.15 ms) +0.5ms (7.6%) 👌
tablecmp-create-10k metric base(165ad3b) target(239c569) trend
benchmark-table-component/create/10k duration 1614.85 (±9.70 ms) 1635.90 (±20.20 ms) +21.1ms (1.3%) 👎
tablecmp-create-1k metric base(165ad3b) target(239c569) trend
benchmark-table-component/create/1k duration 182.75 (±4.65 ms) 186.15 (±5.20 ms) +3.4ms (1.9%) 👌
tablecmp-update-10th-1k metric base(165ad3b) target(239c569) trend
benchmark-table-component/update-10th/1k duration 65.25 (±5.00 ms) 67.75 (±4.05 ms) +2.5ms (3.8%) 👌
wc-append-1k metric base(165ad3b) target(239c569) trend
benchmark-table-wc/append/1k duration 230.45 (±9.65 ms) 234.00 (±8.95 ms) +3.6ms (1.5%) 👌
wc-clear-1k metric base(165ad3b) target(239c569) trend
benchmark-table-wc/clear/1k duration 10.60 (±1.95 ms) 11.25 (±2.05 ms) +0.7ms (6.1%) 👎
wc-create-10k metric base(165ad3b) target(239c569) trend
benchmark-table-wc/create/10k duration 1835.60 (±16.05 ms) 1846.55 (±10.20 ms) +11.0ms (0.6%) 👎
wc-create-1k metric base(165ad3b) target(239c569) trend
benchmark-table-wc/create/1k duration 221.05 (±4.55 ms) 224.85 (±5.65 ms) +3.8ms (1.7%) 👎
wc-update-10th-1k metric base(165ad3b) target(239c569) trend
benchmark-table-wc/update-10th/1k duration 66.10 (±3.70 ms) 66.80 (±4.30 ms) +0.7ms (1.1%) 👌

@jodarove jodarove force-pushed the jodarove/track-rfc-implementation branch from 239c569 to a55fdc4 Compare August 5, 2019 23:15
@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: d5e4be3 | Target commit: a55fdc4

lwc-engine-benchmark

table-append-1k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table/append/1k duration 140.75 (±3.60 ms) 139.85 (±4.05 ms) -0.9ms (0.6%) 👌
table-clear-1k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table/clear/1k duration 10.50 (±1.20 ms) 10.30 (±0.80 ms) -0.2ms (1.9%) 👌
table-create-10k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table/create/10k duration 849.85 (±4.75 ms) 857.45 (±4.70 ms) +7.6ms (0.9%) 👎
table-create-1k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table/create/1k duration 107.25 (±2.55 ms) 107.40 (±2.20 ms) +0.2ms (0.1%) 👌
table-update-10th-1k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table/update-10th/1k duration 75.00 (±5.55 ms) 70.10 (±3.40 ms) -4.9ms (6.5%) 👍
tablecmp-append-1k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table-component/append/1k duration 227.75 (±10.00 ms) 223.65 (±14.00 ms) -4.1ms (1.8%) 👌
tablecmp-clear-1k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table-component/clear/1k duration 6.05 (±1.10 ms) 6.30 (±1.10 ms) +0.2ms (4.1%) 👎
tablecmp-create-10k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table-component/create/10k duration 1594.30 (±12.35 ms) 1641.50 (±15.10 ms) +47.2ms (3.0%) 👎
tablecmp-create-1k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table-component/create/1k duration 186.45 (±3.90 ms) 190.50 (±5.65 ms) +4.1ms (2.2%) 👎
tablecmp-update-10th-1k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table-component/update-10th/1k duration 67.55 (±4.05 ms) 67.75 (±4.35 ms) +0.2ms (0.3%) 👌
wc-append-1k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table-wc/append/1k duration 229.60 (±9.65 ms) 232.35 (±12.30 ms) +2.8ms (1.2%) 👌
wc-clear-1k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table-wc/clear/1k duration 10.65 (±1.75 ms) 11.10 (±1.90 ms) +0.5ms (4.2%) 👌
wc-create-10k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table-wc/create/10k duration 1864.35 (±13.20 ms) 1914.30 (±16.85 ms) +50.0ms (2.7%) 👎
wc-create-1k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table-wc/create/1k duration 220.65 (±4.30 ms) 222.30 (±4.75 ms) +1.7ms (0.7%) 👌
wc-update-10th-1k metric base(d5e4be3) target(a55fdc4) trend
benchmark-table-wc/update-10th/1k duration 67.60 (±4.95 ms) 67.90 (±4.00 ms) +0.3ms (0.4%) 👌

@jodarove jodarove force-pushed the jodarove/track-rfc-implementation branch 2 times, most recently from 557c49e to 48413b4 Compare August 7, 2019 05:24
@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: a40d174 | Target commit: 48413b4

lwc-engine-benchmark

table-append-1k metric base(a40d174) target(48413b4) trend
benchmark-table/append/1k duration 140.90 (±3.90 ms) 142.55 (±4.35 ms) +1.7ms (1.2%) 👌
table-clear-1k metric base(a40d174) target(48413b4) trend
benchmark-table/clear/1k duration 10.35 (±1.05 ms) 10.60 (±0.75 ms) +0.2ms (2.4%) 👌
table-create-10k metric base(a40d174) target(48413b4) trend
benchmark-table/create/10k duration 849.00 (±6.10 ms) 846.25 (±4.35 ms) -2.8ms (0.3%) 👌
table-create-1k metric base(a40d174) target(48413b4) trend
benchmark-table/create/1k duration 108.80 (±2.75 ms) 107.40 (±2.35 ms) -1.4ms (1.3%) 👍
table-update-10th-1k metric base(a40d174) target(48413b4) trend
benchmark-table/update-10th/1k duration 75.65 (±4.40 ms) 76.10 (±4.75 ms) +0.4ms (0.6%) 👌
tablecmp-append-1k metric base(a40d174) target(48413b4) trend
benchmark-table-component/append/1k duration 225.15 (±10.45 ms) 225.80 (±14.40 ms) +0.7ms (0.3%) 👌
tablecmp-clear-1k metric base(a40d174) target(48413b4) trend
benchmark-table-component/clear/1k duration 6.20 (±1.00 ms) 6.30 (±1.05 ms) +0.1ms (1.6%) 👌
tablecmp-create-10k metric base(a40d174) target(48413b4) trend
benchmark-table-component/create/10k duration 1627.95 (±17.20 ms) 1603.35 (±8.95 ms) -24.6ms (1.5%) 👍
tablecmp-create-1k metric base(a40d174) target(48413b4) trend
benchmark-table-component/create/1k duration 186.35 (±4.75 ms) 185.40 (±4.85 ms) -0.9ms (0.5%) 👌
tablecmp-update-10th-1k metric base(a40d174) target(48413b4) trend
benchmark-table-component/update-10th/1k duration 68.10 (±4.55 ms) 70.15 (±4.60 ms) +2.1ms (3.0%) 👌
wc-append-1k metric base(a40d174) target(48413b4) trend
benchmark-table-wc/append/1k duration 232.85 (±12.95 ms) 229.15 (±10.30 ms) -3.7ms (1.6%) 👌
wc-clear-1k metric base(a40d174) target(48413b4) trend
benchmark-table-wc/clear/1k duration 10.65 (±1.85 ms) 10.50 (±1.85 ms) -0.1ms (1.4%) 👌
wc-create-10k metric base(a40d174) target(48413b4) trend
benchmark-table-wc/create/10k duration 1840.60 (±14.30 ms) 1863.40 (±15.90 ms) +22.8ms (1.2%) 👎
wc-create-1k metric base(a40d174) target(48413b4) trend
benchmark-table-wc/create/1k duration 227.85 (±3.55 ms) 223.05 (±3.95 ms) -4.8ms (2.1%) 👍
wc-update-10th-1k metric base(a40d174) target(48413b4) trend
benchmark-table-wc/update-10th/1k duration 66.85 (±5.55 ms) 66.95 (±5.90 ms) +0.1ms (0.1%) 👌

@jodarove jodarove removed the work-in-progress Work in progress label Aug 7, 2019
@jodarove jodarove requested review from diervo, pmdartus and caridy August 7, 2019 19:11
@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: a40d174 | Target commit: c95de19

lwc-engine-benchmark

table-append-1k metric base(a40d174) target(c95de19) trend
benchmark-table/append/1k duration 140.90 (±3.90 ms) 141.45 (±3.00 ms) +0.5ms (0.4%) 👌
table-clear-1k metric base(a40d174) target(c95de19) trend
benchmark-table/clear/1k duration 10.35 (±1.05 ms) 10.70 (±0.90 ms) +0.3ms (3.4%) 👌
table-create-10k metric base(a40d174) target(c95de19) trend
benchmark-table/create/10k duration 849.00 (±6.10 ms) 850.55 (±5.45 ms) +1.5ms (0.2%) 👌
table-create-1k metric base(a40d174) target(c95de19) trend
benchmark-table/create/1k duration 108.80 (±2.75 ms) 108.90 (±3.45 ms) +0.1ms (0.1%) 👌
table-update-10th-1k metric base(a40d174) target(c95de19) trend
benchmark-table/update-10th/1k duration 75.65 (±4.40 ms) 74.75 (±5.35 ms) -0.9ms (1.2%) 👌
tablecmp-append-1k metric base(a40d174) target(c95de19) trend
benchmark-table-component/append/1k duration 225.15 (±10.45 ms) 229.80 (±9.10 ms) +4.7ms (2.1%) 👌
tablecmp-clear-1k metric base(a40d174) target(c95de19) trend
benchmark-table-component/clear/1k duration 6.20 (±1.00 ms) 6.35 (±0.90 ms) +0.1ms (2.4%) 👌
tablecmp-create-10k metric base(a40d174) target(c95de19) trend
benchmark-table-component/create/10k duration 1627.95 (±17.20 ms) 1632.05 (±15.55 ms) +4.1ms (0.3%) 👌
tablecmp-create-1k metric base(a40d174) target(c95de19) trend
benchmark-table-component/create/1k duration 186.35 (±4.75 ms) 188.10 (±4.45 ms) +1.8ms (0.9%) 👌
tablecmp-update-10th-1k metric base(a40d174) target(c95de19) trend
benchmark-table-component/update-10th/1k duration 68.10 (±4.55 ms) 69.50 (±6.10 ms) +1.4ms (2.1%) 👌
wc-append-1k metric base(a40d174) target(c95de19) trend
benchmark-table-wc/append/1k duration 232.85 (±12.95 ms) 227.95 (±9.25 ms) -4.9ms (2.1%) 👌
wc-clear-1k metric base(a40d174) target(c95de19) trend
benchmark-table-wc/clear/1k duration 10.65 (±1.85 ms) 11.05 (±1.90 ms) +0.4ms (3.8%) 👌
wc-create-10k metric base(a40d174) target(c95de19) trend
benchmark-table-wc/create/10k duration 1840.60 (±14.30 ms) 1855.40 (±18.25 ms) +14.8ms (0.8%) 👎
wc-create-1k metric base(a40d174) target(c95de19) trend
benchmark-table-wc/create/1k duration 227.85 (±3.55 ms) 225.65 (±5.60 ms) -2.2ms (1.0%) 👌
wc-update-10th-1k metric base(a40d174) target(c95de19) trend
benchmark-table-wc/update-10th/1k duration 66.85 (±5.55 ms) 66.10 (±4.95 ms) -0.8ms (1.1%) 👌

@@ -89,6 +89,7 @@ export interface UninitializedVM {
cmpProps: any;
cmpSlots: SlotSet;
cmpTrack: any;
cmpFields: any;
Copy link
Contributor

Choose a reason for hiding this comment

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

don't add a new one... use cmpTrack instead because in another PR (wire reform), I'm renaming this to cmpFields as a single place to store such values.

@@ -231,6 +232,7 @@ export function createVM(elm: HTMLElement, Ctor: ComponentConstructor, options:
context: create(null),
cmpProps: create(null),
cmpTrack: create(null),
cmpFields: create(null),
Copy link
Contributor

Choose a reason for hiding this comment

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

remove

@@ -154,6 +158,7 @@ function createComponentDef(
name,
wire,
track,
fields,
Copy link
Contributor

Choose a reason for hiding this comment

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

this is only needed if you need to know what are the fields later on... which I don't think you need to, you just install the descriptor and forget about them. remove it.

import { defineProperty, isFalse } from '../shared/language';

export function observeFields(Ctor: ComponentConstructor, fields: string[] | undefined) {
if (fields) {
Copy link
Contributor

Choose a reason for hiding this comment

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

!isUndefined(fields)

in fact I think that this condition should happens in the caller of this method... and this method, if invoked, should always have at least one field.

Copy link
Contributor

Choose a reason for hiding this comment

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

this method should also receive the proto, instead of the Ctor, that's easier to reason about... you pass the obj that receive the descriptors... alternative, you call this method with the field list... and it returns a PropertyDescriptorMap that then you install on any obj, I like that better, it disconnects this from the ctor completely.

Copy link
Contributor

@caridy caridy Aug 9, 2019

Choose a reason for hiding this comment

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

you can rename this method to createObservableFieldsMap(fields: PropertyKey[]): PropertyDescriptorMap

!isRendering,
`${vmBeingRendered}.render() method has side effects on the state of ${vm}.${String(
key
)}`
Copy link
Contributor

Choose a reason for hiding this comment

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

... state of foo field.

Copy link
Contributor

@caridy caridy left a comment

Choose a reason for hiding this comment

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

few minor stylish changes... but the logic is ready.

RFC for this change: salesforce/lwc-rfcs#4

The logic for this PR is as follows:

When the compiler compiles the class, it extracts any field that is not decorated with @api, @track or @wire, and pass the metadata through the registerDecorators call.

Given that in order to observe a field, we need a vm, and we need to register the fields of all classes and save it in the decoratorsMeta, we need to wait until we create the ComponentDefinition to know which classes will be used as components.

For every class field we will observe in a Component, we will create a getter and a setter in the prototype of the ComponentDefinition (and the class inheritance chain until BaseLightningElement).

No change is needed to the logic in the engine.
@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: a0a0862 | Target commit: 4a6a167

@jodarove jodarove force-pushed the jodarove/track-rfc-implementation branch from 4a6a167 to f4bb16d Compare August 9, 2019 05:19
import { isFalse } from '../shared/language';

export function createObservableFieldsDescriptorMap(fields: PropertyKey[]): PropertyDescriptorMap {
return fields.reduce((acc, field) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Use ArrayReduce from language

Copy link
Member

@ekashida ekashida left a comment

Choose a reason for hiding this comment

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

Looks good overall besides the inconsistency between the terms "observable" and "observed" (the latter seems more correct to me). It does feel weird to me that we're reusing decorator plumbing to implement implicitly-tracked fields, but maybe that's because of outdated naming (e.g., DecoratorMeta should really be ComponentMeta, etc).

packages/@lwc/engine/src/framework/observable-fields.ts Outdated Show resolved Hide resolved
packages/@lwc/engine/src/framework/observable-fields.ts Outdated Show resolved Hide resolved
packages/@lwc/engine/src/framework/def.ts Outdated Show resolved Hide resolved
assert.isTrue(vm && 'cmpRoot' in vm, `${vm} is not a valid vm.`);
assert.invariant(
!isRendering,
`${vmBeingRendered}.render() method has side effects on the state of "${String(
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Isn't the call to String(key) implicit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the type of key is PropertyKey which is string | number | symbol and the implicit conversion of symbol will fail at runtime, thus, the String()

Co-Authored-By: Eugene Kashida <[email protected]>

Update packages/integration-karma/test/component/observed-fields/index.spec.js

Co-Authored-By: Eugene Kashida <[email protected]>

Update packages/@lwc/engine/src/framework/def.ts

Co-Authored-By: Eugene Kashida <[email protected]>

Update packages/@lwc/engine/src/framework/observable-fields.ts

Co-Authored-By: Eugene Kashida <[email protected]>

Update packages/@lwc/engine/src/framework/observable-fields.ts

Co-Authored-By: Eugene Kashida <[email protected]>

Update packages/@lwc/babel-plugin-component/src/post-process/transform.js

Co-Authored-By: Eugene Kashida <[email protected]>
@jodarove jodarove force-pushed the jodarove/track-rfc-implementation branch from e50a78b to 726f03c Compare August 9, 2019 22:02
@jodarove
Copy link
Contributor Author

jodarove commented Aug 9, 2019

@ekashida @caridy this ready for another pass.

@ekashida yes, i agree with you, registerDecorators is looking more to registerComponentMeta, i believe @caridy idea is to tackle this in another effort.

}

export interface DecoratorMeta {
wire: WireHash | undefined;
track: TrackDef;
props: PropsDef;
methods: MethodDef;
fields?: string[];
Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer the other annotation, fields: string[] | undefined, technically it is almost the same, but the shape of the meta obj should always have the same properties, even if they are set to undefined.

Copy link
Contributor

@caridy caridy left a comment

Choose a reason for hiding this comment

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

small nip, let's roll! this is good to go!

Copy link
Member

@ekashida ekashida left a comment

Choose a reason for hiding this comment

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

nice work!

@ekashida
Copy link
Member

Where are the perf numbers?

@caridy
Copy link
Contributor

caridy commented Aug 11, 2019

@ekashida the perf numbers are only going to be posted as a comment when there is a regression, otherwise it will be accessible via the checks, the details for BEST task.

@@ -162,6 +162,7 @@ function createComponentDef(
name,
wire,
track,
fields: undefined,
Copy link
Contributor

Choose a reason for hiding this comment

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

this is not needed right? we can remove this from Def completely.

Copy link
Contributor Author

@jodarove jodarove Aug 12, 2019

Choose a reason for hiding this comment

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

@caridy this is needed with your suggested change: fields: string[] | undefined createComponentDef receives a ComponentDef which inherit from DecoratorsMeta, and now fields can be string[] or undefined, but should be present.

@jodarove jodarove merged commit 2dcaa8c into master Aug 12, 2019
@jodarove jodarove deleted the jodarove/track-rfc-implementation branch August 12, 2019 19:29
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.

3 participants