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

Clean up CodeGenerator by moving compilation-global data and logic to Context #1190

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

mzabaluev
Copy link
Contributor

@mzabaluev mzabaluev commented Nov 19, 2024

Refactoring improvements to prost-build:

  • Move compilation-global context data and logic around it from CodeGenerator to a new Context struct.
  • Lift similar logic into Context from MessageGraph, where it caused some data duplication.
  • Resolve some awkwardness about the Config reference needing to be mutable for its user-provided service generator, by exposing both from Context in a more disciplined way.

@mzabaluev
Copy link
Contributor Author

mzabaluev commented Nov 19, 2024

I would like this to be merged before I add an enum type registry, which is necessary to properly implement imports (and later with editions, the enum_type feature) for #1079.

Copy link
Collaborator

@caspermeijn caspermeijn left a comment

Choose a reason for hiding this comment

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

I see plenty of internal changes that I like: better naming, cleanup of boxed, more helper functions.

  • Is it possible to separate those from the API changes? Then we can that merge that before doing a version number bump
  • Is it possible to separate each improvement into a separate commit? That will make reviewing easier.

buf: &mut String,
) {
impl<'a, 'b> CodeGenerator<'a, 'b> {
pub fn generate(context: &mut Context<'b>, file: FileDescriptorProto, buf: &mut String) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is an API breaking change, right? I don't understand how the Context instance should be created by prost users as there is no documentation changes included.

Copy link
Contributor Author

@mzabaluev mzabaluev Nov 20, 2024

Choose a reason for hiding this comment

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

No, CodeGenerator is private to the crate, as is the newly added Context.

pub struct CodeGenerator<'a> {
config: &'a mut Config,
pub struct CodeGenerator<'a, 'b> {
context: &'a mut Context<'b>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand why some fields are in CodeGenerator, Context or Config. For example, depth also feels like it could be part of Context.

Can you explain when a field should be in each of those structs?

Copy link
Contributor Author

@mzabaluev mzabaluev Nov 20, 2024

Choose a reason for hiding this comment

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

Config is the public settings configured by the API user.

Context is a private helper that captures data global to the compilation requests: the message graph, the map of extern paths, and the reference to the Config. I also plan to add a map of enum openness flags for a proper implementation of open enums in #1079.
It is populated before code generation and the instance is shared between successive calls to CodeGenerator::generate for the requested members. This replaces several arguments that did the same thing with the disparate data structures.

The owned members of CodeGenerator manage state specific to code generation for a proto file. So depth belongs there.

/// It also provides a more disciplined access to Config
/// and its mutable instance of ServiceGenerator.
pub struct Context<'a> {
config: &'a mut Config,
Copy link
Collaborator

Choose a reason for hiding this comment

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

In the PR description, you mention that a mutable Config is awkward. But it is still mut can you further explain what has been improved?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The improvement is in the accessor methods below. CodeGenerator cannot access the private members of Context, so, even though its reference to Context is mutable, ...

pub struct CodeGenerator<'a> {
config: &'a mut Config,
pub struct CodeGenerator<'a, 'b> {
context: &'a mut Context<'b>,
Copy link
Contributor Author

@mzabaluev mzabaluev Nov 20, 2024

Choose a reason for hiding this comment

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

Config is the public settings configured by the API user.

Context is a private helper that captures data global to the compilation requests: the message graph, the map of extern paths, and the reference to the Config. I also plan to add a map of enum openness flags for a proper implementation of open enums in #1079.
It is populated before code generation and the instance is shared between successive calls to CodeGenerator::generate for the requested members. This replaces several arguments that did the same thing with the disparate data structures.

The owned members of CodeGenerator manage state specific to code generation for a proto file. So depth belongs there.

/// It also provides a more disciplined access to Config
/// and its mutable instance of ServiceGenerator.
pub struct Context<'a> {
config: &'a mut Config,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The improvement is in the accessor methods below. CodeGenerator cannot access the private members of Context, so, even though its reference to Context is mutable, ...

}
}

pub fn config(&self) -> &Config {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

... it can only get an immutable reference to Config via this method,...

self.config
}

pub fn service_generator(&mut self) -> Option<&mut (dyn ServiceGenerator + 'static)> {
Copy link
Contributor Author

@mzabaluev mzabaluev Nov 20, 2024

Choose a reason for hiding this comment

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

... but with this method, it can get a reference to the mutable object providing a ServiceGenerator vtable. Which was the only reason for passing a mutable reference to Config to CodeGenerator before, even though it does not need to (and really should not) mutate any other members of Config.

@mzabaluev mzabaluev changed the title Clean up CodeGenerator by moving global data and code to Context Clean up CodeGenerator by moving compilation-global data and logic to Context Nov 20, 2024
Move global context data and logic around it from CodeGenerator
to a new Context struct. Also move the logic of can_message_derive_copy
and can_field_derive_copy from MessageGraph where it caused some
data duplication.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants