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

fix(ivy): allow abstract directives to have an invalid constructor #32987

Closed
wants to merge 1 commit into from

Conversation

JoostK
Copy link
Member

@JoostK JoostK commented Oct 3, 2019

For abstract directives, i.e. directives without a selector, it may
happen that their constructor is called explicitly from a subclass,
hence its parameters are not required to be valid for Angular's DI
purposes. Prior to this commit however, having an abstract directive
with a constructor that has parameters that are not eligible for
Angular's DI would produce a compilation error.

A similar scenario may occur for @Injectables, where an explicit
use* definition allows for the constructor to be irrelevant. For
example, the situation where useFactory is specified allows for the
constructor to be called explicitly with any value, so its constructor
parameters are not required to be valid. For @Injectables this is
handled by generating a DI factory function that throws.

This commit implements the same solution for abstract directives, such
that a compilation error is avoided while still producing an error at
runtime if the type is instantiated implicitly by Angular's DI
mechanism.

Fixes #32981


TODO

  • Adjust error message for abstract directives, it currently refers to @Injectable

@JoostK JoostK added type: bug/fix state: WIP effort1: hours hotlist: components team Related to Angular CDK or Angular Material workaround4: none freq1: low severity3: broken target: major This PR is targeted for the next major release risk: medium area: compiler Issues related to `ngc`, Angular's template compiler labels Oct 3, 2019
@ngbot ngbot bot modified the milestone: Backlog Oct 3, 2019
@ngbot ngbot bot modified the milestone: Backlog Oct 3, 2019
@@ -133,6 +133,21 @@ export function valueReferenceToExpression(
}
}

export function unwrapConstructorDependencies(deps: ConstructorDeps | null): R3DependencyMetadata[]|
Copy link
Member Author

Choose a reason for hiding this comment

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

Note to self: maybe this should not have been extracted into a function, but rather repeated at the call sites as it previously was. The reason being that the pending TODO is not applicable to abstract directives, so this shared function may not be desirable.

Copy link
Member

Choose a reason for hiding this comment

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

Nope, this LGTM :) I just changed the comments.

@JoostK
Copy link
Member Author

JoostK commented Oct 4, 2019

One more thing we should take into account: throwing an error with a descriptive message is quite heavy on code size, so we may want to look into alternatives.

@trotyl
Copy link
Contributor

trotyl commented Oct 4, 2019

For abstract directives, i.e. directives without a selector

This left no rooms for possible runtime directive in future, which could always be selector-less. Selector-less and base/abstract are conceptually not the same thing, a concrete component can be selector-less, as long as not meant for template usage (entry component only).

@JoostK JoostK force-pushed the ngtsc-abstract-dir-ctor branch 2 times, most recently from 3ddfa57 to b9c1c31 Compare October 7, 2019 19:04
@JoostK
Copy link
Member Author

JoostK commented Oct 10, 2019

We came up with an idea to generate ngFactoryDef = null; instead of a function that always throws, as that is better suitable regarding code size and still allows an error with descriptive message to be produced form within the runtime itself. This avoids duplicating the error message potentially multiple times in a single bundle, and the message in the runtime can be shortened/left out in prod builds.

@alxhub alxhub marked this pull request as ready for review October 25, 2019 00:32
@alxhub alxhub requested review from a team as code owners October 25, 2019 00:32
@alxhub
Copy link
Member

alxhub commented Oct 25, 2019

Presubmit
Ivy Presubmit


describe('abstract directives', () => {
it('should generate a factory function that throws', () => {
env.tsconfig({strictInjectionParameters: false});
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'm not sure if I wrote this or this was added by @alxhub, but we should definitely have a test with strictInjectionParameters: true to know that it will not fail at compile time for abstract directives, instead generating ɵɵinvalidFactory (which is unlike the result for concrete directives, which should fail to compile in that case).

Copy link
Member

Choose a reason for hiding this comment

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

Added :)

export interface R3FactoryDefMetadata {
name: string;
type: o.Expression;
typeArgumentCount: number;
deps: R3DependencyMetadata[]|'invalid'|null;
injectFn: o.ExternalReference;
isPipe?: boolean;
target: R3FactoryTarget;
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 changed this to become an enum to be able to tweak the error message, now that we generate the invalidFactory instruction that is not strictly necessary anymore. However, I still like this change so it can stay in, but be aware that these changes somehow causes breaking tests that end up with an infinite recursive call during change detection. This is still the case today, and I have no clue how this happens.

Copy link
Member

Choose a reason for hiding this comment

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

Fixed. JIT directive compilation was adding a factory intended for pipes, which uses a different ChangeDetectorRef implementation.

@AndrewKushnir
Copy link
Contributor

@JoostK @alxhub there are some failing CircleCI jobs, could you please have a look? Thank you.

@JoostK JoostK requested a review from a team as a code owner October 25, 2019 16:43
For abstract directives, i.e. directives without a selector, it may
happen that their constructor is called explicitly from a subclass,
hence its parameters are not required to be valid for Angular's DI
purposes. Prior to this commit however, having an abstract directive
with a constructor that has parameters that are not eligible for
Angular's DI would produce a compilation error.

A similar scenario may occur for `@Injectable`s, where an explicit
`use*` definition allows for the constructor to be irrelevant. For
example, the situation where `useFactory` is specified allows for the
constructor to be called explicitly with any value, so its constructor
parameters are not required to be valid. For `@Injectable`s this is
handled by generating a DI factory function that throws.

This commit implements the same solution for abstract directives, such
that a compilation error is avoided while still producing an error at
runtime if the type is instantiated implicitly by Angular's DI
mechanism.

Fixes angular#32981
@alxhub
Copy link
Member

alxhub commented Oct 25, 2019

Presubmit
Ivy Presubmit

@alxhub alxhub added action: merge The PR is ready for merge by the caretaker merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note labels Oct 25, 2019
@alxhub
Copy link
Member

alxhub commented Oct 25, 2019

Caretaker: public API is unaffected (addition of a private API)

@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 Nov 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker area: compiler Issues related to `ngc`, Angular's template compiler cla: yes effort1: hours freq1: low hotlist: components team Related to Angular CDK or Angular Material merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note risk: medium target: major This PR is targeted for the next major release type: bug/fix workaround4: none
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Different dependency injection behavior for abstract directives in Ivy
5 participants