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

Ivy: DI decorators tree-shaking when they are used to configure useFactory deps #40143

Closed
AndrewKushnir opened this issue Dec 16, 2020 · 2 comments
Assignees
Labels
area: core Issues related to the framework runtime cross-cutting: tree-shaking P4 A relatively minor issue that is not relevant to core functions refactoring Issue that involves refactoring or code-cleanup
Milestone

Comments

@AndrewKushnir
Copy link
Contributor

AndrewKushnir commented Dec 16, 2020

Angular DI allows providing DI tokens using useFactory function, which can accepts arguments configured via deps array.

Here is a quick example:

@Component({
    selector: 'comp',
    template: '...',
    viewProviders: [{
        provide: 'B',
        deps: [[new Inject('A'), new SkipSelf()]],
        useFactory: (token: string) => token,
    }]
})
class Comp {}

The underlying logic that constructs these args refers to DI decorators (such as SkipSelf, Self, etc) explicitly, thus making them non-tree-shakable:

for (let j = 0; j < arg.length; j++) {
const meta = arg[j];
if (meta instanceof Optional || meta.ngMetadataName === 'Optional' || meta === Optional) {
flags |= InjectFlags.Optional;
} else if (
meta instanceof SkipSelf || meta.ngMetadataName === 'SkipSelf' || meta === SkipSelf) {
flags |= InjectFlags.SkipSelf;
} else if (meta instanceof Self || meta.ngMetadataName === 'Self' || meta === Self) {
flags |= InjectFlags.Self;
} else if (meta instanceof Inject || meta === Inject) {
type = meta.token;
} else {
type = meta;
}
}

Instead, we should consider using an approach of patching a field (like __NG_DI_FLAG___) onto a class and update the mentioned logic to check for this flag instead.

      for (let j = 0; j < arg.length; j++) {
        const meta = arg[j];
        const metaFlags = meta.__NG_DI_FLAG__;
        if (flags === undefined) {
          type = meta;
        } else if (typeof metaFlags === 'number') {
          flags |= metaFlags;
        } else {
          // `meta instanceof Inject || meta === Inject` case:
          type = meta.token;
        }
      }

Important:

  • this should not cause and runtime perf issues, since we already have a megamorphic calls (meta.ngMetadataName), the new approach will reduce the number of calls from 4 to 1.
  • we should verify that skipping the meta.ngMetadataName check here is critical and break any existing use-cases.
  • the __NG_DI_FLAG__ flag should be present on both class definition and class instance:
Inject.__NG_DI_FLAG__ = ...;
(new Inject()).__NG_DI_FAG__ = ...;

As a result, DI decorators should only be present in the bundle when they are used explicitly in the app code.

@AndrewKushnir AndrewKushnir added refactoring Issue that involves refactoring or code-cleanup area: core Issues related to the framework runtime cross-cutting: tree-shaking labels Dec 16, 2020
@ngbot ngbot bot modified the milestone: needsTriage Dec 16, 2020
@AndrewKushnir AndrewKushnir added the P4 A relatively minor issue that is not relevant to core functions label Dec 16, 2020
@ngbot ngbot bot modified the milestones: needsTriage, Backlog Dec 16, 2020
@AndrewKushnir
Copy link
Contributor Author

Related PR that triggered investigation - #40122.

@AndrewKushnir AndrewKushnir self-assigned this Dec 16, 2020
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Dec 16, 2020
…y` deps config

This commit updates the logic that calculates `useFactory` function arguments to avoid relying on `instanceof`
checks (thus always retaining symbols) and relies on specific flags that DI decorators contain.

Closes angular#40143.
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jan 8, 2021
…y` deps config

This commit updates the logic that calculates `useFactory` function arguments to avoid relying on `instanceof`
checks (thus always retaining symbols) and relies on specific flags that DI decorators contain.

Closes angular#40143.
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jan 9, 2021
…y` deps config

This commit updates the logic that calculates `useFactory` function arguments to avoid relying on `instanceof`
checks (thus always retaining symbols) and relies on flags that DI decorators contain (as a monkey-patched property).

Closes angular#40143.
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jan 11, 2021
…y` deps config

This commit updates the logic that calculates `useFactory` function arguments to avoid relying on `instanceof`
checks (thus always retaining symbols) and relies on flags that DI decorators contain (as a monkey-patched property).

Another perf benefit is having less megamorphic reads while calculating args for the `useFactory` call: we used to
check whether a token has `ngMetadataName` property 4 times (in worst case), now we have just 1 megamorphic read in
all cases.

Closes angular#40143.
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jan 13, 2021
…y` deps config

This commit updates the logic that calculates `useFactory` function arguments to avoid relying on `instanceof`
checks (thus always retaining symbols) and relies on flags that DI decorators contain (as a monkey-patched property).

Another perf benefit is having less megamorphic reads while calculating args for the `useFactory` call: we used to
check whether a token has `ngMetadataName` property 4 times (in worst case), now we have just 1 megamorphic read in
all cases.

Closes angular#40143.
AndrewKushnir added a commit to AndrewKushnir/angular that referenced this issue Jan 13, 2021
…y` deps config

This commit updates the logic that calculates `useFactory` function arguments to avoid relying on `instanceof`
checks (thus always retaining symbols) and relies on flags that DI decorators contain (as a monkey-patched property).

Another perf benefit is having less megamorphic reads while calculating args for the `useFactory` call: we used to
check whether a token has `ngMetadataName` property 4 times (in worst case), now we have just 1 megamorphic read in
all cases.

Closes angular#40143.
atscott pushed a commit that referenced this issue Jan 13, 2021
…y` deps config (#40145)

This commit updates the logic that calculates `useFactory` function arguments to avoid relying on `instanceof`
checks (thus always retaining symbols) and relies on flags that DI decorators contain (as a monkey-patched property).

Another perf benefit is having less megamorphic reads while calculating args for the `useFactory` call: we used to
check whether a token has `ngMetadataName` property 4 times (in worst case), now we have just 1 megamorphic read in
all cases.

Closes #40143.

PR Close #40145
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Feb 13, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: core Issues related to the framework runtime cross-cutting: tree-shaking P4 A relatively minor issue that is not relevant to core functions refactoring Issue that involves refactoring or code-cleanup
Projects
None yet
1 participant