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

2.x - Performance optimizations geared towards legacy Polymer({...}) use #5418

Merged
merged 42 commits into from
Nov 27, 2018

Conversation

sorvell
Copy link
Contributor

@sorvell sorvell commented Oct 31, 2018

  • Behaviors:
    • Now mixed in lazily rather than at class time. This change means behaviors are no longer in the element's prototype chain.
    • A memoized list of behavior lifecycle methods is constructed at finalization time and then used at runtime.
  • Adds Polymer.legacyOptimizations flag which avoids copying element templates (needed for subclassing) and turns on stripWhitespace for all templates.
  • TemplateStamp: A TreeWalker is now used to crawl through template nodes. This should be slightly faster when the ShadyDOM polyfill is used.
  • Polymer.telemetry: now properly records registered elements, even if they have not been created

kevinpschaaf and others added 12 commits October 19, 2018 18:35
Set using `Polymer.setLegacyOptimizations(true)`. This flag enables optimizaitons including, always stripping whitespace from templates and avoiding cloning element templates (assumes no inheritance is used)

Also re-enables dir-mixin.
* fix issue where registered was called on element instance instead of prototype
* fix issue where applyListeners was called in constructor rather than initializeProperties; this made `disable-upgrade` mixin not properly work with litsners.
* properly de-dup behaviors from superclasses
* apply only "own" behaviors to class
templateStyle.parentNode.insertBefore(s, templateStyle);
} else {
templateStyleIndex++;
if (!window.skipStyleIncludesAndUrls) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than putting this on window, shouldn't we put this in the Polymer.Settings object?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, thanks. Going to get this window.ShadyCSS.cssBuild being set (which will shortly be added to ShadyCSS.

});

test('elements upgrade when `disable-upgrade` removed', function() {
assert.notOk(el.$.disabledEl.hasCreated);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we not remove these assertions or are they regressions? Are they a breaking change for our users?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These were actually duplicate assertions from the previous test (https://github.com/Polymer/polymer/pull/5418/files#diff-ed6f0a354a50488549e9b8ea17788d86R240) that were incorrectly being tested again.

@@ -552,7 +551,13 @@
});

test('nested-behavior dedups', function() {
assert.equal(el.behaviors.length, 4);
assert.equal(el.behaviors.length, 2);
Copy link
Contributor

Choose a reason for hiding this comment

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

This also seems like a regression to me. Are we sure users will not run into any breaking changes?

Copy link
Contributor 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 regression since the test changed, but the behavior is weird and I think we should change it.

We didn't have a test where Polymer.mixinBehaviors was called on a legacy element with behaviors (since this is pretty exotic) and this test was just updated to do that.

When that happens, the behaviors list is set to the subclasses' list of behaviors. However, the "active" set of behaviors on the element is the de-duplicated flat list of all behaviors on anything in the prototype chain. Given that, it seems like .behaviors should be this de-duplicated flattened list of "active" behaviors. We can make that change and update the test.

Steven Orvell added 4 commits October 31, 2018 18:03
* Uses `window.ShadyCSS.cssBuild` to avoid doing unnecessary work in `processElementStyles`
* Stores the list of active in-use behaviors in a legacy element's `behaviors` property. This only matters in the exotic case when elements with behaviors are extended with more behaviors and previously the list included only the subclasses behaviors (although the element otherwise worked as expected).
sorvell pushed a commit that referenced this pull request Nov 1, 2018
Steven Orvell added 5 commits November 1, 2018 12:45
@sorvell sorvell changed the title Performance optimizations geared towards legacy Polymer({...}) use 2.x - Performance optimizations geared towards legacy Polymer({...}) use Nov 1, 2018
GenerateClassFromInfo(b, klass);
function copyBehaviorProperties(behaviors, klass) {
const meta = {};
const superMeta = klass.prototype.__behaviorMetaProps;
Copy link
Member

Choose a reason for hiding this comment

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

Since these are all lifecycle-related, maybe __behaviorCallbacks would be better than something with "props" in it, since that's sort of overloaded with all the data stuff.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed.

}

/**
* @return {void}
*/
created() {
super.created();
const list = this.__behaviorMetaProps.created;
Copy link
Member

Choose a reason for hiding this comment

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

Consider adding info. from the base info to the list?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

mergeProperties(properties, b.properties);
}
}
mergeProperties(properties, info.properties);
Copy link
Member

Choose a reason for hiding this comment

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

It seemed like a possible optimization to skip the mergeProperties step from the base info object into an empty object; it seems superfulous for the initial class -- but then I realized this was required for the computed to readOnly coersion and the shorthand type expansion. So that was not intuitive, maybe due to the naming? Suggest different name or at least a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added comment.

klass.prototype.__behaviorMetaProps = meta;
}

function memoizeBehaviorMetaProps(meta, behavior, superMeta) {
Copy link
Member

Choose a reason for hiding this comment

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

There's a bug here where the full flattened list of lifecycle callbacks is called once per call to GenerateClassFromInfo, which happens each time mixinBehaviors is called.

Instead, store the list of callbacks in the closure for one set of behaviors associated with each call to GenerateClassFromInfo.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed and test added.

/* NOTE: `beforeRegister` is called here for bc, but the behavior
is different than in 1.x. In 1.0, the method was called *after*
mixing prototypes together but *before* processing of meta-objects.
However, dynamic effects can still be set here and can be done either
in `beforeRegister` or `registered`. It is no longer possible to set
`is` in `beforeRegister` as you could in 1.x.
*/
const proto = this;
if (proto.hasOwnProperty('__behaviors')) {
Copy link
Member

Choose a reason for hiding this comment

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

This is just what's in the closure, i.e.
if (behaviors) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

behaviors: true
}, metaProps);

const memoizedProps = Object.assign({
Copy link
Member

Choose a reason for hiding this comment

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

Naming of these is confusing. Consider "memoized" --> "filtered"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -256,11 +256,10 @@
<script>
HTMLImports.whenReady(function() {
customElements.define('nested-behaviors',
class extends Polymer.mixinBehaviors(
class extends Polymer.mixinBehaviors([window.BehaviorD, window.LifeCycleBehavior1], Polymer.mixinBehaviors(
Copy link
Member

Choose a reason for hiding this comment

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

Add assertions for BehaviorD?

@kevinpschaaf
Copy link
Member

Also maybe add a test like

const b1 = { ready() {..} };
const b2 = { ready() {..} };
const b3 = { ready() {..} };
const b4 = { ready() {..} };
const sup = Polymer({is: 'sup', behaviors: [b1, b2], ready() {..} })
class sub extends mixinBehaviors([b3,b4], sup) { ready() {..} }

And verify that the lifecycle order goes:

sub, b4, b3, sup, b2, b1

Steven Orvell added 5 commits November 6, 2018 12:27
This ensures it's not called on extendors who do not specifically implement `_registered`. This is important since it's expected to do prototype specific work.
sorvell pushed a commit that referenced this pull request Nov 6, 2018
Copy link
Member

@kevinpschaaf kevinpschaaf left a comment

Choose a reason for hiding this comment

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

LGTM!

Steven Orvell and others added 14 commits November 9, 2018 11:39
This better matches what Polymer 1.x did for:
* hostAttributes
* listeners
* properties
* observers
…present.

This ensures ShadyCSS scoping classes are not removed when a class binding's initial literal value is set.
* ensure element has `is` on prototype early as this is sometimes checked in user code.
* ensure properties copied onto elements from info/behaviors are forced to configurable so they can be re-configured by later behaviors.
* add `_noAccessors` optimization for faster property copying
Previously, it could be called on a superclass' prototype and not the element's prototype.
* When extended, a behavior `registered` is always called on sub-most prototype rather than the prototype on which `registered` was defined.
* behavior property default values are overwritten by later behaviors and elements
* readOnly properties ignored when a previous behavior observes the property
* observedAttributes when extended
* Don't set up observer in ShadyDOM
* Move __activateDir into check instead of replace
* skip some tests that never really worked in ShadyDOM
@kevinpschaaf kevinpschaaf merged commit 1b5849e into 2.x Nov 27, 2018
@kevinpschaaf kevinpschaaf deleted the perf-opt branch November 27, 2018 01:34
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.

4 participants