сhore(agtree): add node-specific clone utility#186
сhore(agtree): add node-specific clone utility#186SatoruFF wants to merge 1 commit intoAdguardTeam:masterfrom
Conversation
scripthunter7
left a comment
There was a problem hiding this comment.
Thanks for putting together this PR, but I think it's a bit too early to merge it. There is already a task for this in AGTree v5 (which you also linked), and we're planning to implement it at the appropriate stage.
It's important to note that v5 will also modify and improve the node types to some extent, and we’re not planning to add this kind of feature to v4 anymore.
| // raws only has primitive fields (text, nl), but it's still an object reference | ||
| type Raws = NonNullable<import('../nodes').RuleBase['raws']>; | ||
| function cloneRaws(raws: Raws | undefined): Raws | undefined { | ||
| return raws ? { ...raws } : undefined; |
There was a problem hiding this comment.
I wrote a small benchmark script that showed it's more efficient to list properties explicitly rather than using the spread operator, enumerating properties is about 1.5x faster, but depends on the platform. If we want to avoid generic solutions anyway, it's a better idea to go with that approach.
| function cloneHtmlFilteringRuleBody(node: HtmlFilteringRuleBody): HtmlFilteringRuleBody { | ||
| const selectorList: SelectorList = { | ||
| ...node.selectorList, | ||
| children: node.selectorList.children.map((complexSelector) => ({ |
There was a problem hiding this comment.
Instead of using map, it's more efficient to preallocate an array of the same length and fill it using a simple for loop without any spread operator.
| result.params = cloneScriptletRuleNode(node.params); | ||
| } else { | ||
| // expression nodes are rare and have no dedicated clone path yet | ||
| result.params = structuredClone(node.params); |
There was a problem hiding this comment.
Generally, we should avoid using structuredClone where possible, as it is quite slow, although this can be platform-dependent. It's interesting by the way, because at first you wouldn't expect this, since it's a native implementation, but even clone-deep can outperform it :)
Closes? #153
The converters were using
clone-deepto copy rule nodes before mutating them.Since we know the exact shape of every node type, a generic deep-clone is overkill —
so this adds node-specific clone functions instead.
What's added
cloneAnyRule— main entry point, dispatches bynode.categorycloneAnyCommentRule,cloneAnyNetworkRule,cloneAnyCosmeticRule— typed genericsso the specific subtype is preserved at call sites
cloneScriptletRuleNodeetc.) are untouchedWhat's changed
Replaced
clone(rule)+ TODO comments in three converter files with the new functions.Tests
test/ast-utils/clone.test.ts— covers all rule categories, checks both value equalityand that no object references are shared between original and clone.