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
Optional chaining of method calls may generate invalid instructions #44069
Comments
|
I think that this is indeed a bug in how the Angular expression is being converted to TS in the ivy handler. The following expression a?.fn(test(form.get('test')?.value))is converted to let tmp_b_0;
ctx.a == null ? null : ctx.a.fn(ctx.test((tmp_b_0 = tmp_b_0) == null ? null : tmp_b_0.value));while the following expression test(form.get('test')?.value)is converted to ctx.test((tmp_b_0 = ctx.form.get("test")) == null ? null : tmp_b_0.value);Note that in the first case we have: ctx.test((tmp_b_0 = tmp_b_0) == null ? null : tmp_b_0.value)while in the second: ctx.test((tmp_b_0 = ctx.form.get("test")) == null ? null : tmp_b_0.value);So in the first case tmp_b_0 is always undefined... |
|
Yeah I noticed that too. So this is a bug. A pretty big one for my code base. |
|
I have a hunch this got broken in #42882. I'll be looking at a fix. |
When a safe method call such as `person?.getName()` is used, the
compiler would generate invalid code if the argument list also contained
a safe access. For example, the following code:
```
person?.getName(config?.get('title').enabled)
```
would generate
```
let tmp;
ctx.person == null ? null : ctx.person.getName((tmp = tmp) == null ?
null : tmp.enabled)
```
Notice how the call to `config.get('title')` has completely disappeared,
with `(tmp = tmp)` having taken its place.
The issue occurred due to how the argument list would be converted
from expression AST to output AST twice. First, the outer safe method
call would first convert its arguments list. This resulted in a
temporary being allocated for `ctx.config.get('title')`, which was
stored in the internal `_resultMap`. Only after the argument list has
been converted would the outer safe method call realize that it should
be guarded by a safe access of `person`, entering the
`convertSafeAccess` procedure to convert itself. This would convert the
argument list once again, but this time the `_resultMap` would already
contain the temporary `tmp` for `config?.get('title')`. Consequently,
the safe method in the argument list would be emitted as `tmp`.
This commit fixes the issue by ensuring that nodes are only converted
once.
Closes angular#44069
When a safe method call such as `person?.getName()` is used, the
compiler would generate invalid code if the argument list also contained
a safe method call. For example, the following code:
```
person?.getName(config?.get('title').enabled)
```
would generate
```
let tmp;
ctx.person == null ? null : ctx.person.getName((tmp = tmp) == null ?
null : tmp.enabled)
```
Notice how the call to `config.get('title')` has completely disappeared,
with `(tmp = tmp)` having taken its place.
The issue occurred due to how the argument list would be converted
from expression AST to output AST twice. First, the outer safe method
call would first convert its arguments list. This resulted in a
temporary being allocated for `config.get('title')`, which was stored in
the internal `_resultMap`. Only after the argument list has been
converted would the outer safe method call realize that it should be
guarded by a safe access of `person`, entering the `convertSafeAccess`
procedure to convert itself. This would convert the argument list once
again, but this time the `_resultMap` would already contain the
temporary `tmp` for `config?.get('title')`. Consequently, the safe
method in the argument list would be emitted as `tmp`.
This commit fixes the issue by ensuring that nodes are only converted
once.
Closes angular#44069
When a safe method call such as `person?.getName()` is used, the
compiler would generate invalid code if the argument list also contained
a safe method call. For example, the following code:
```
person?.getName(config?.get('title').enabled)
```
would generate
```
let tmp;
ctx.person == null ? null : ctx.person.getName((tmp = tmp) == null ?
null : tmp.enabled)
```
Notice how the call to `config.get('title')` has completely disappeared,
with `(tmp = tmp)` having taken its place.
The issue occurred due to how the argument list would be converted
from expression AST to output AST twice. First, the outer safe method
call would first convert its arguments list. This resulted in a
temporary being allocated for `config.get('title')`, which was stored in
the internal `_resultMap`. Only after the argument list has been
converted would the outer safe method call realize that it should be
guarded by a safe access of `person`, entering the `convertSafeAccess`
procedure to convert itself. This would convert the argument list once
again, but this time the `_resultMap` would already contain the
temporary `tmp` for `config?.get('title')`. Consequently, the safe
method in the argument list would be emitted as `tmp`.
This commit fixes the issue by ensuring that nodes are only converted
once.
Closes angular#44069
When a safe method call such as `person?.getName()` is used, the
compiler would generate invalid code if the argument list also contained
a safe method call. For example, the following code:
```
person?.getName(config?.get('title').enabled)
```
would generate
```
let tmp;
ctx.person == null ? null : ctx.person.getName((tmp = tmp) == null ?
null : tmp.enabled)
```
Notice how the call to `config.get('title')` has completely disappeared,
with `(tmp = tmp)` having taken its place.
The issue occurred due to how the argument list would be converted
from expression AST to output AST twice. First, the outer safe method
call would first convert its arguments list. This resulted in a
temporary being allocated for `config.get('title')`, which was stored in
the internal `_resultMap`. Only after the argument list has been
converted would the outer safe method call realize that it should be
guarded by a safe access of `person`, entering the `convertSafeAccess`
procedure to convert itself. This would convert the argument list once
again, but this time the `_resultMap` would already contain the
temporary `tmp` for `config?.get('title')`. Consequently, the safe
method in the argument list would be emitted as `tmp`.
This commit fixes the issue by ensuring that nodes are only converted
once.
Closes #44069
PR Close #44088
|
Thank for the quick fix! Any info on when a new release will be cut? |
|
Typically on Wednesdays (US timezone) |
|
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
…4088) When a safe method call such as `person?.getName()` is used, the compiler would generate invalid code if the argument list also contained a safe method call. For example, the following code: ``` person?.getName(config?.get('title').enabled) ``` would generate ``` let tmp; ctx.person == null ? null : ctx.person.getName((tmp = tmp) == null ? null : tmp.enabled) ``` Notice how the call to `config.get('title')` has completely disappeared, with `(tmp = tmp)` having taken its place. The issue occurred due to how the argument list would be converted from expression AST to output AST twice. First, the outer safe method call would first convert its arguments list. This resulted in a temporary being allocated for `config.get('title')`, which was stored in the internal `_resultMap`. Only after the argument list has been converted would the outer safe method call realize that it should be guarded by a safe access of `person`, entering the `convertSafeAccess` procedure to convert itself. This would convert the argument list once again, but this time the `_resultMap` would already contain the temporary `tmp` for `config?.get('title')`. Consequently, the safe method in the argument list would be emitted as `tmp`. This commit fixes the issue by ensuring that nodes are only converted once. Closes angular#44069 PR Close angular#44088


Which @angular/* package(s) are the source of the bug?
forms
Is this a regression?
Yes
Description
When passing a form control value to a function that is end the end of an optional chain, the value is
null.Expected (works in v12), console:
Thu Nov 04 2021 22:42:39 GMT+0100 (CET) – Thu Nov 04 2021 22:42:39 GMT+0100 (CET)Actual in v13:
Thu Nov 04 2021 22:42:39 GMT+0100 (CET) – nullThis problem does not occur when using
!instead of?on the form value, or when using the value outside a function that is part of an optional chain.Please provide a link to a minimal reproduction of the bug
No response
Please provide the exception or error you saw
Please provide the environment you discovered this bug in
Anything else?
Checked exact same sample in latest Angular 12, no problems there.
The text was updated successfully, but these errors were encountered: