Skip to content

Begin replacing propertyInfo.* access with functions #11733

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

Closed
wants to merge 16 commits into from

Conversation

gaearon
Copy link
Collaborator

@gaearon gaearon commented Dec 1, 2017

The goal is to remove the "injection" step at initialization. I find it confusing because we always have to look in three places: the configs, the initialization logic, and then the use of those properties. I think this is partly why the logic got so tangled by now.

I'm starting by removing the propertyInfo.* properties one by one, replacing them with function calls. There might be better implementations—happy to revise them. In some cases it may seem like we'd have to repeat an attribute name a few more times than before, but I think it probably gzips well, and in any case I'll make sure the size is smaller or the same before merging.

Note I'm not changing logic yet. There are a few oddities in how code paths differ based on whether propertyInfo exists—which ideally shouldn't be the case. I'll looking at that later.

// different attribute names (see DOMAttributeNames below)
// different attribute names (see `attributeNames`)
// TODO: this doesn't seem great? Probably means we depend on
// existence in the whitelist somewhere we shouldn't need to.
Copy link
Contributor

Choose a reason for hiding this comment

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

I remember when we hit this adding custom attributes, and it didn't feel great. It would be awesome to get to the bottom of this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yea, definitely will. I have another set of commits that dives into this but it turned out to be a rabbit hole and I decided to do these first.

multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
muted: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
multiple: HAS_BOOLEAN_VALUE,
muted: HAS_BOOLEAN_VALUE,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is probably fine, but why the change?

Copy link
Collaborator Author

@gaearon gaearon Dec 1, 2017

Choose a reason for hiding this comment

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

Not sure what you mean? I removed the flag.
c9ca797
Easier to read as individual commits.

Copy link
Contributor

Choose a reason for hiding this comment

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

Got it. Thank you!

Copy link
Contributor

Choose a reason for hiding this comment

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

@gaearon at this point it looks like there aren't any properties that contain more than one flag. We could probably stop using bitmasks and just use an enum, remove checkMask and use a single property in propertyInfo instead of 6.

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 plan is to get rid of propertyInfo completely (maybe). This PR is just very iterative.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a good observation though.

var attributeName = propertyInfo.attributeName;
var namespace = propertyInfo.attributeNamespace;
var attributeName = getAttributeName(name);
var namespace = getAttributeNamespace(name);
// `setAttribute` with objects becomes only `[object]` in IE8/9,
// ('' + value) makes it output the correct toString()-value.
Copy link
Contributor

@nhunzaker nhunzaker Dec 1, 2017

Choose a reason for hiding this comment

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

Is this true in IE9?

screen shot 2017-11-30 at 8 02 51 pm

Copy link
Contributor

Choose a reason for hiding this comment

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

Also valueOf:

screen shot 2017-11-30 at 8 03 38 pm

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Maybe only IE8?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

(Regardless, don't want to change the logic here, only the structure :-)

Copy link
Contributor

Choose a reason for hiding this comment

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

Totally. It's just nice to look at this code again with a fresh set of eyes. I'll file an issue.

Copy link
Contributor

Choose a reason for hiding this comment

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


export function shouldUseProperty(name) {
return usePropertiesFor.has(name);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this just be inlined in DOMPropertyOperations? Doesn't look like it's used anywhere else. Also, we could then just do check the set directly instead of a function call too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I want to consolidate all property/attribute stuff here first, then refactor it to make function contracts clearer, then possibly spread it out. I'd rather not spread it out yet.

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 just easier for me to deal with it if it's all in one file. Easier to see patterns.

var attributeName = propertyInfo.attributeName;
var namespace = propertyInfo.attributeNamespace;
var attributeName = getAttributeName(name);
var namespace = getAttributeNamespace(name);
// `setAttribute` with objects becomes only `[object]` in IE8/9,
// ('' + value) makes it output the correct toString()-value.
Copy link
Contributor

Choose a reason for hiding this comment

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

Internally they're still implemented as flags.
But this is now an implementation detail I can change.
Now that all propertyInfo access is internal to DOMProperty, we can stop exposing
the property info. This shows the code paths that are different depending on whether
something is whitelisted or not. These are bad because ideally our other helper functions
should work just fine regardless of the whitelist (they just should have correct defaults
for things that aren't in the list). We will need to start getting rid of these calls.
This removes the existing duplication.
My previous changed caused it to access the fields multiple times.
Now that it's encapsulated inside DOMProperty, I can implement it efficiently again.
Instead, keep a Map of name to type constant.
The only real mask at this point was positive numeric + numeric, so I used switch fallthrough.
@gaearon
Copy link
Collaborator Author

gaearon commented Dec 1, 2017

@aweary Indeed it's much nicer without masks, thanks for the tip.

// An attribute that can be used as a flag as well as with a value.
// Removed when strictly equal to false; present without a value when
// strictly equal to true; present with a value otherwise.
const OVERLOADED_BOOLEAN = 5;
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 💯

@aweary
Copy link
Contributor

aweary commented Dec 1, 2017

@gaearon glad it worked out, it looks great!

At this point, I wonder if we could just get rid of HTMLDOMPropertyConfig and initialize the propertyTypes map with those values? Then we can get rid of injectDOMPropertyConfig in favor of something SVG-specific since those are the only values that are dynamically generated at runtime.

We'd lose out on the injectDOMPropertyConfig warning, but I'm not sure if it has a lot of value anyways since we're not exporting injection logic right?

@gaearon
Copy link
Collaborator Author

gaearon commented Dec 1, 2017

At this point, I wonder if we could just get rid of HTMLDOMPropertyConfig and initialize the propertyTypes map with those values?

Yes but we'll need to see what's more compact in terms of bundle size. I'm not sure.

@aweary
Copy link
Contributor

aweary commented Dec 1, 2017

Yes but we'll need to see what's more compact in terms of bundle size. I'm not sure.

@gaearon I checked this out locally and made the change to compare bundle sizes.

By inlining the config the NODE_PROD build for react-dom.production.min.js went from 29.39kb to 29.34kb, and the UMD build went from 30.49kb to 30.46kb. So inlining will also reduce the bundle size a little!

@gaearon
Copy link
Collaborator Author

gaearon commented Dec 1, 2017

Nice. Please push to my branch?

These changes seem to make the results neutral again (after a small regression).
propertyInfo.hasOverloadedBooleanValue
);
if (propertyTypes.has(name)) {
switch (propertyTypes.get(name)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the overhead of the switch greater than the double lookup of has and get?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was trying to avoid making it polymorphic (number/undefined). Maybe that's silly, dunno.

'multiple',
'muted',
'selected',
]);
Copy link
Contributor

@nhunzaker nhunzaker Dec 1, 2017

Choose a reason for hiding this comment

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

I know microbenchmarks are dangerous, but would indexOf or object property membership be faster?

https://esbench.com/bench/5a21e22199634800a03494ec

screen shot 2017-12-01 at 6 15 58 pm

Copy link
Contributor

Choose a reason for hiding this comment

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

I bet in the long-term checking set membership will be faster, since that's the sole purpose of the data structure. If we're still getting 25 million ops/s with a set it's probably fine to use it since its the correct data structure for this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ya. I hesitated to say anything. Dan just mentioned the extra look-ups were slowing things down a bit.

Feel free to disregard.

Copy link
Contributor

Choose a reason for hiding this comment

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

If it's a non-trivial performance regression we should definitely consider changing it. It's just so nice using Set and Map instead of arrays and objects 😍

@gaearon
Copy link
Collaborator Author

gaearon commented Dec 2, 2017

Meh. It's too slow. I need to rethink this.

@gaearon
Copy link
Collaborator Author

gaearon commented Dec 10, 2017

#11815

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants