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

Make ReflectionGenerator::getFunction() legal after generator termination #14167

Merged
merged 8 commits into from
May 21, 2024

Conversation

TimWolla
Copy link
Member

@TimWolla TimWolla commented May 7, 2024

Currently after termination, a Generator loses all its information, except for the return value (if any). This makes it hard to impossible to introspect where that return value is coming from, for example in debuggers.

This PR adds an explicit zend_function* to the struct _zend_generator to allow the retrieval of the function even after termination. This new pointer allows ReflectionGenerator::getFunction() to work correctly at all times. As a side-benefit I was able to add the function name to the __debugInfo(), making it more easily available without going through Reflection, allowing users to distinguish different Generators at a glance.

@TimWolla
Copy link
Member Author

TimWolla commented May 7, 2024

Draft while investigating the ASAN failures. Didn't catch them locally, due to --asan missing from my run-tests.php call.

@TimWolla TimWolla marked this pull request as ready for review May 7, 2024 16:05
Copy link
Member

@Girgias Girgias left a comment

Choose a reason for hiding this comment

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

I think this makes sense

Zend/zend_generators.c Outdated Show resolved Hide resolved
Zend/zend_generators.c Show resolved Hide resolved
@TimWolla TimWolla requested a review from iluuu1994 May 13, 2024 06:44
@dstogov
Copy link
Member

dstogov commented May 13, 2024

As a side-benefit I was able to add the function name to the __debugInfo(), making it more easily available without going through Reflection, allowing users to distinguish different Generators at a glance.

I'm not sure if this makes any benefits, but this is a behavior change and should go through RFC.

Change to ReflectionGenerator::getFunction() may be considered as a fix.

Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

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

The patch leads to crashes on few tests.

It's not possible to directly instantiate the Generator class [Zend/tests/generators/errors/generator_instantiate_error.phpt]
Try to instantiate all classes without arguments [Zend/tests/instantiate_all_classes.phpt]
Bug #64007 (There is an ability to create instance of Generator by hand) [ext/reflection/tests/bug64007.phpt]

@TimWolla TimWolla force-pushed the generator-get-function-after-end branch from d0afb9e to 0852151 Compare May 13, 2024 08:59
@TimWolla
Copy link
Member Author

The patch leads to crashes on few tests.

Now fixed, thank you. This was a regression after Ilija's review that I didn't catch locally, because the tests do not contain a yield (which is what I used to filter for Generator tests).

@TimWolla
Copy link
Member Author

I'm not sure if this makes any benefits, but this is a behavior change and should go through RFC.

I believe the addition of a new __debugInfo() handler should fall under the “small self-contained feature” policy. I'm happy to write an RFC if you believe that one is necessary, but I feel that this would unnecessarily take up the voter’s time for such a simple change. Please confirm if I should proceed with an RFC, then I'll split out the __debugInfo() change into a separate PR and leave this one with just the ReflectionGenerator::getFunction() fix.

@dstogov
Copy link
Member

dstogov commented May 13, 2024

I'm not sure if this makes any benefits, but this is a behavior change and should go through RFC.

I believe the addition of a new __debugInfo() handler should fall under the “small self-contained feature” policy. I'm happy to write an RFC if you believe that one is necessary, but I feel that this would unnecessarily take up the voter’s time for such a simple change. Please confirm if I should proceed with an RFC, then I'll split out the __debugInfo() change into a separate PR and leave this one with just the ReflectionGenerator::getFunction() fix.

I wouldn't consider this as a self-contained change, because it is exposed to user (var_dump output).
I don't care about this a lot, and I won't object if other core developers would support this change.

Copy link
Member

@iluuu1994 iluuu1994 left a comment

Choose a reason for hiding this comment

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

I think this should also adjust getExecutingFile, which doesn't need to rely on execute_data anymore.

This looks good to me otherwise. I don't think changes like these should require RFCs. Adding something to the debug output is barely a change. The technical aspects would be better evaluated by a technical committee than internals, but that proposal was rejected.

Zend/zend_generators.h Outdated Show resolved Hide resolved
Zend/zend_generators.c Show resolved Hide resolved
@TimWolla
Copy link
Member Author

I think this should also adjust getExecutingFile, which doesn't need to rely on execute_data anymore.

I've considered that, but I'm not sure how useful that is without getExecutingLine(). The naming also implies that the generator must currently be executing, which it no longer is once it terminated.

The filename would also already be available via $r->getFunction()->getFileName().

@iluuu1994
Copy link
Member

iluuu1994 commented May 17, 2024

I think, it's better to allow than to throw when there's no technical reason. The function name is just a bit of a misnomer. But I don't care too much.

@TimWolla
Copy link
Member Author

The function name is just a bit of a misnomer.

At least getExecutingLine() follows the generator’s actual execution:

https://3v4l.org/WYp9l

While of course a single generator can’t execute in different files, it makes sense to me that getExecutingFile() is a companion to getExecutingLine(), rather than a getDefiningFile().

@bwoebi
Copy link
Member

bwoebi commented May 17, 2024

@dstogov I don't think this change is significant enough to require a RFC and consensus is fine.

I support this change, provides a bit more visibility.

Copy link
Member

@nielsdos nielsdos left a comment

Choose a reason for hiding this comment

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

I feel like this PR could provide an (in)direct way of solving #11591
But I'd have to check after merging if we can utilise your functionality.

This changes debug info output, but debug info output changes have happened before without requiring an RFC. At worst, some test cases would have to be adjusted so unless there's complaints I don't think this needs an RFC.

And I agree with Tim about the getExecutingLine() / getExecutingFile() thing.

Zend/zend_generators.c Outdated Show resolved Hide resolved
Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

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

I don't object

@TimWolla TimWolla force-pushed the generator-get-function-after-end branch from 830eee7 to 9cc2357 Compare May 21, 2024 06:48
@TimWolla TimWolla merged commit 8094bd1 into php:master May 21, 2024
7 of 10 checks passed
@TimWolla TimWolla deleted the generator-get-function-after-end branch May 21, 2024 06:54
@alexandre-daubois
Copy link
Contributor

alexandre-daubois commented May 27, 2024

This PR break Symfony CI because it relies on a ReflectionException here:

https://github.com/symfony/symfony/blob/2e0dafae4d20c39580b4ac0df9e574a6182e5a72/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php#L81-L93

Creating a ReflectionGenerator on a closed generator throws prior to this change, which isn't the case any more. Is it considered a breaking change in userland?

@iluuu1994
Copy link
Member

@alexandre-daubois Thanks for letting us know. What is the result of the test? Does castGenerator now work for these cases too? Or does it behave unexpectedly?

@alexandre-daubois
Copy link
Contributor

alexandre-daubois commented May 27, 2024

Here is the update I proposed to the framework: https://github.com/symfony/symfony/pull/57191/files

As you can see, the dump format has changed (which is fine). castGenerator() used to throw at new ReflectionGenerator() when the generator is closed. The vardumper would then display closed: true and that's it.

With this PR, instantiating a ReflectionGenerator on a closed generator doesn't throw anymore. castReflectionGenerator() can now be executed (which wasn't executed when new ReflectionGenerator() threw), making the script fail at this line: https://github.com/symfony/symfony/blob/7ddc5ece5788a54345c494948b1ca775e821ef11/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php#L137. The message is something like Cannot gather information on a closed generator.

This is why I moved the call to self::castReflectionGenerator() in the try block.

nicolas-grekas added a commit to symfony/symfony that referenced this pull request May 27, 2024
…bois)

This PR was merged into the 5.4 branch.

Discussion
----------

[VarDumper] Fix generator dump on PHP 8.4

| Q             | A
| ------------- | ---
| Branch?       | 5.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        | -
| License       | MIT

From PHP 8.4, generators dump contains the generating function: php/php-src#14167.

Also, creating a ReflectionGenerator on a closed generator is now permitted, which explains the update in `ReflectionCaster` to catch anything that may happen in `castReflectionGenerator()`.

Commits
-------

fc0dc0e [VarDumper] Fix generator dump on PHP 8.4
@TimWolla
Copy link
Member Author

TimWolla commented May 27, 2024

@alexandre-daubois Would an explicit ReflectionGenerator::isFinished() (or similar) be helpful to you?

@alexandre-daubois
Copy link
Contributor

It would be a nice addition for sure! Definitely a good idea, cleaner than checking for an exception. I think in the case of this Symfony class, it could help to refactor a bit but the patch I proposed actually works well.
But if someone relies on an ReflectionException as well in its code to know if a generator is closed, going from 8.3 to 8.4 will break the application.

TimWolla added a commit to TimWolla/php-src that referenced this pull request May 29, 2024
TimWolla added a commit that referenced this pull request May 29, 2024
* reflection: Add `ReflectionGenerator::isClosed()`

see #14167 (comment)

* Fix test expectation

* Drop `{{{` / `}}}` comments around `ReflectionGenerator::isClosed()`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants