Skip to content

Remove namespace import for objUtils #2807

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

Merged
merged 12 commits into from
Mar 31, 2020
Merged

Conversation

schmidt-sebastian
Copy link
Contributor

@schmidt-sebastian schmidt-sebastian commented Mar 26, 2020

This PR removes the namespace import for objUtils and replaces a bunch of usages of the helper functions with ES6 standard primitives (Objects -> Maps).

I verified that the object that I am now using Object.keys()/Object.values() on don't have any inherited properties.

@google-oss-bot
Copy link
Contributor

google-oss-bot commented Mar 26, 2020

Binary Size Report

Affected SDKs

SDKTypeBase (50de3fb)Head (1bb1a67)Diff
@firebase/firestore/memorybrowser224500.00224475.00-25.00 (-0.01%)
module221680.00221655.00-25.00 (-0.01%)
esm2017170334.00170304.00-30.00 (-0.02%)
main397701.00397404.00-297.00 (-0.07%)
@firebase/firestorebrowser271073.00271055.00-18.00 (-0.01%)
module267852.00267834.00-18.00 (-0.01%)
esm2017216152.00216141.00-11.00 (-0.01%)
main488427.00488418.00-9.00 (-0.00%)
firebasefirebase.js846912.00846872.00-40.00 (-0.00%)
firebase-firestore.memory.js266794.00266750.00-44.00 (-0.02%)
firebase-firestore.js312035.00311995.00-40.00 (-0.01%)
Metric Unit: byte

Test Logs

@schmidt-sebastian
Copy link
Contributor Author

Better diff for viewing: #2807

@@ -912,7 +914,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
const activeTargets: TargetId[] = [];

let p = Promise.resolve();
objUtils.forEachNumber(this.queriesByTarget, (targetId, _) => {
this.queriesByTarget.forEach((_, targetId) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could these forEach loops be replaced with simpler and, arguable, more readable,for loops?

e.g.

this.queriesByTarget.forEach((_, targetId) => {

could be changed to:

for (targetId of this.queriesByTarget.keys()) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried making this work (I pushed two commits and one revert). The problem is that I would have to turn on "downlevelIteration" to make this work with ES5, which adds a lot of code (+4%).

The example you posted actually does not need downlevelIteration since you are converting the Map to an Array. That creates an extra copy though and hence I decided to leave this as is.

Copy link
Contributor

Choose a reason for hiding this comment

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

Cool. Thanks for looking into this.

@@ -72,7 +72,7 @@ const encodedEscape = '\u0011';
/**
* Encodes a resource path into a IndexedDb-compatible string form.
*/
export function encode(path: ResourcePath): EncodedResourcePath {
export function encodeResourcePath(path: ResourcePath): EncodedResourcePath {
Copy link
Contributor

Choose a reason for hiding this comment

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

What is your rationale for renaming these functions? It's not that I disagree, it's just that the motivation is unclear to me.

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 are now used as top-level functions. I renamed them to be more descriptive than just "encode/decode".

Copy link
Contributor

Choose a reason for hiding this comment

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

That's what I figured. Makes sense.

@schmidt-sebastian
Copy link
Contributor Author

@wilhuff for approval.

@@ -912,7 +914,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
const activeTargets: TargetId[] = [];

let p = Promise.resolve();
objUtils.forEachNumber(this.queriesByTarget, (targetId, _) => {
this.queriesByTarget.forEach((_, targetId) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Cool. Thanks for looking into this.

@@ -72,7 +72,7 @@ const encodedEscape = '\u0011';
/**
* Encodes a resource path into a IndexedDb-compatible string form.
*/
export function encode(path: ResourcePath): EncodedResourcePath {
export function encodeResourcePath(path: ResourcePath): EncodedResourcePath {
Copy link
Contributor

Choose a reason for hiding this comment

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

That's what I figured. Makes sense.

@@ -57,8 +57,8 @@ export class NodePlatform implements Platform {
}

formatJSON(value: unknown): string {
// util.inspect() results in much more readable output than JSON.stringify()
return util.inspect(value, { depth: 100 });
// util's inspect() results in much more readable output than JSON.stringify()
Copy link
Contributor

Choose a reason for hiding this comment

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

util.inspect previously matched JSON.stringify. Now these are inconsistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't follow this. Can you elaborate? JSON.stringify is a global and doesn't need to be (cannot be?) imported.

Copy link
Contributor

Choose a reason for hiding this comment

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

What I meant was that in the comment you have "util's inspect()" but "JSON.stringify()". Spelling the former as "util.inspect()" would keep the text references consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, got it. Fixed.

@@ -41,7 +41,7 @@ export class RemoteEvent {
/**
* A map from target to changes to the target. See TargetChange.
*/
readonly targetChanges: { [targetId: number]: TargetChange },
readonly targetChanges: Map<TargetId, TargetChange>,
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we avoided ES 2015 Map because it wasn't available everywhere. Has this changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Map is actually widely available, just some of the support for individual methods is lacking (and our solution for that are Polyfills).

Even IE11 understands Map for the most part: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

We also use Map in one other place:

type Overlay = Map<string, Overlay> | api.Value | null;
(recently introduced in the field value rewrite). I was actually surprised we don't use it in more places.

@@ -596,11 +595,11 @@ export class WatchChangeAggregator {
}

private ensureTargetState(targetId: TargetId): TargetState {
if (!this.targetStates[targetId]) {
this.targetStates[targetId] = new TargetState();
if (!this.targetStates.has(targetId)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

You could make this a single Map access in the common case and avoid the force unwrap by rewriting this as:

let result = this.targetStates.get(targetId);
if (result !== undefined) {
  result = new TargetState();
  this.targetStates.set(targetId, result);
}
return result;

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

if (
objUtils.contains(settingsLiteral as objUtils.Dict<{}>, 'persistence')
) {
if (settingsLiteral.hasOwnProperty('persistence')) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If the user has overridden hasOwnProperty this validation result cannot be trusted. Also, if the object was created with Object.create(null) the object has no prototype and this invocation will throw. Calling Object.prototype.hasOwnProperty.call is the safer way to phrase this. Since we do this in a few places and the reference can't be minified, it's probably cheaper to keep the contains method.

Since this is really validation-related rather than general object manipulation it could make sense to move contains into input_validation.ts.

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. The only usage that is sensible is in this file, so I moved contains here.

@@ -103,7 +103,7 @@ class TestSharedClientSyncer implements SharedClientStateSyncer {

get sharedClientState(): TestSharedClientState {
return {
mutationCount: objUtils.size(this.mutationState),
mutationCount: size(this.mutationState),
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 a really ambiguous name now. Maybe objectSize?

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

@@ -442,14 +438,14 @@ export class SpecBuilder {

// Clear any preexisting limbo watch targets, which we'll re-create as
// necessary from the provided keys below.
objUtils.forEach(this.limboMapping, (key, targetId) => {
forEach(this.limboMapping, (key, targetId) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

You could map limboMapping a Map as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are a lot of data structures the spec tests that can be converted - but not all of them (since some of them are used in the JSON output). For my sanity, I decided to only clean up the types that relied on the "utils" methods that I removed.

private expectedActiveTargets: {
[targetId: number]: { queries: SpecQuery[]; resumeToken: string };
};
private expectedActiveTargets: Map<
Copy link
Contributor

Choose a reason for hiding this comment

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

This type is repeated several times in here. Would it make sense to create a type alias for the map type or even just the value type within the map?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated here and in another place (used the key only as it is more widely used).

@wilhuff wilhuff assigned schmidt-sebastian and unassigned wilhuff Mar 30, 2020
@schmidt-sebastian schmidt-sebastian removed their assignment Mar 31, 2020
Copy link
Contributor

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

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

LGTM

@@ -57,8 +57,8 @@ export class NodePlatform implements Platform {
}

formatJSON(value: unknown): string {
// util.inspect() results in much more readable output than JSON.stringify()
return util.inspect(value, { depth: 100 });
// util's inspect() results in much more readable output than JSON.stringify()
Copy link
Contributor

Choose a reason for hiding this comment

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

What I meant was that in the comment you have "util's inspect()" but "JSON.stringify()". Spelling the former as "util.inspect()" would keep the text references consistent.

@wilhuff wilhuff assigned schmidt-sebastian and unassigned wilhuff Mar 31, 2020
@schmidt-sebastian schmidt-sebastian merged commit 8e70e4a into master Mar 31, 2020
@schmidt-sebastian schmidt-sebastian deleted the mrschmidt/inline branch April 6, 2020 19:26
@firebase firebase locked and limited conversation to collaborators May 1, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants