Skip to content

Introduce getSynthesizedDeepClone #18979

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 7 commits into from
Oct 11, 2017
Merged

Introduce getSynthesizedDeepClone #18979

merged 7 commits into from
Oct 11, 2017

Conversation

amcasey
Copy link
Member

@amcasey amcasey commented Oct 5, 2017

During refactoring, it seems quite common to require that a node appear in more than one place in the rewritten tree.

@amcasey
Copy link
Member Author

amcasey commented Oct 6, 2017

Apparently, this is a public API change. What's the correct way to handle that?

@weswigham
Copy link
Member

If it's desirable, just accept the new API baselines.

@ghost
Copy link

ghost commented Oct 6, 2017

Or mark it @internal like getSynthesizedClone.

@amcasey
Copy link
Member Author

amcasey commented Oct 6, 2017

@Andy-MS's right - it was an oversight on my part.

/* @internal */
export function getSynthesizedDeepClone<T extends Node>(node: T | undefined): T | undefined {
return node
? getSynthesizedClone(visitEachChild(node, child => getSynthesizedDeepClone(child), nullTransformationContext))
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to write child => getSynthesizedDeepClone(child) when just getSynthesizedDeepClone would do.

*/
/* @internal */
export function getSynthesizedDeepClone<T extends Node>(node: T | undefined): T | undefined {
return node
Copy link
Contributor

Choose a reason for hiding this comment

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

This whole operation seems somewhat expensive. The call to visitEachChild will likely resolve in a call to one of the updateX functions in factory.ts, which in turn calls updateNode, which in turn sets the original pointer on the new node, clones the original node's emitNode, and sets the pos and end of the new node. Then getSynthesizedClone discards the new object for a fresh one.

If we only need this for entity-name-like expressions, could we instead write a more specific version that doesn't do all of this extra work that is just thrown out? See serializeQualifiedNameAsExpression in ts.ts for a function that already does this (albeit with some extra options you don't need).

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree that's it's overkill for this application. It was actually added for #18997 and this was a proof-of-concept application. If there's a way to make this less expensive, I'd be happy to rework it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Since visitEachChild guarantees it returns a new node if any of the children change, you can use that to ensure the result is a clone by doing this instead:

function getSynthesizedDeepClone<T extends Node>(node: T | undefined): T | undefined {
    const visited = visitEachChild(node, getSynthesizedDeepClone, nullTransformationContext);
    if (visited === node) return getSynthesizedClone(node);
    visited.pos = -1;
    visited.end = -1;
    visited.parent = undefined;
    return visited;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, consider moving this to services so that it isn't used unintentionally in compiler.

@amcasey
Copy link
Member Author

amcasey commented Oct 10, 2017

@rbuckton I believe I've addressed your feedback.

@amcasey
Copy link
Member Author

amcasey commented Oct 10, 2017

@rbuckton - I believe most/all callers will want to copy trivia. Does that mean deep-clone should actually be built on getMutableClone, rather than getSynthesizedClone?

@rbuckton
Copy link
Contributor

I'd rather not use getMutableClone as I'd like to eventually get rid of that function. You could always call setTextRange in your getDeepSynthesizedClone to copy positions.

@amcasey
Copy link
Member Author

amcasey commented Oct 11, 2017

@rbuckton Could I just refrain from clearing pos and end? (I assume I still have to clear parent.)

@amcasey
Copy link
Member Author

amcasey commented Oct 11, 2017

As of this PR, the positions don't make a difference. I'll add a test once #18931 is merged.

@amcasey
Copy link
Member Author

amcasey commented Oct 11, 2017

@rbuckton signed off offline.

@amcasey amcasey merged commit bada009 into microsoft:master Oct 11, 2017
@amcasey amcasey deleted the DeepClone branch October 11, 2017 23:36
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants