Skip to content

When can macros inspect the body of functions? #2185

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

Closed
rrousselGit opened this issue Apr 2, 2022 · 7 comments
Closed

When can macros inspect the body of functions? #2185

rrousselGit opened this issue Apr 2, 2022 · 7 comments
Labels
static-metaprogramming Issues related to static metaprogramming

Comments

@rrousselGit
Copy link

cf https://github.com/dart-lang/language/blob/master/working/macros/feature-specification.md#phase-3-definitions

The doc of phase 2 explicitly mentions:

"In this phase, macros declare functions, variables, and members. "Declaring" here means specifying the name and type signature, but not the body of a function or initializer for a variable."

I assume that phase 3 will be able to, but the document doesn't explicitly mentions it.
Considering I will need the ability to inspect the body of functions, would it be possible to clarify whether macros will/won't be able to?

@jakemac53
Copy link
Contributor

jakemac53 commented Apr 4, 2022

There is no plan as of right now to allow introspection on the bodies of functions.

I think it should still be feasible, at least to allow you to inspect the body of the immediately annotated function. But note that it means you would not see the augmentations of that function (may be desirable or not). However this would be a large amount of additional API and work to expose.

Am I correct in understanding that the primary use case here is to collect references to instance members in the body of a function? I wonder if we could provide a more explicit api to just get exactly that information?

@jakemac53 jakemac53 added the static-metaprogramming Issues related to static metaprogramming label Apr 4, 2022
@rrousselGit
Copy link
Author

Am I correct in understanding that the primary use case here is to collect references to instance members in the body of a function? I wonder if we could provide a more explicit api to just get exactly that information?

That is correct.
I have numerous use-cases where I want to statically obtain the list of dependencies of a function.

I don't need the ability to inspect augmentations of that function. My use-case is meant to be statically analyzable.

My current workaround is to write custom analyzer rules:

Screen.Recording.2022-06-14.at.22.50.39.1.mov

But that's a bit weird.
By relying on lint rules, that dependencies variable is basically generated code that leaves in the userland.

I'd like to be able to straight-up remove that parameter

@rrousselGit
Copy link
Author

rrousselGit commented Jul 25, 2022

Coming back to this, I have another important use-case for code introspection @jakemac53:

Stateful hot reload

I would like a macro to generate a hash of all the combined source code of a function (including the content invoked).
The idea is that this hash would then be used during developement to determine after a hot-reload if the source of a given macro usage has changed

The idea is that the package would rely on a getter/function

@macro
void fn() {

}

// generated
String _$internalToPackage() => 'this is a hash of the source of fn';

A package would then cache those hashes. And whenever a hot-reload is performed, the package would rerun the various hash functions, and compare the previous vs new result.
Then, if the result has changed, the state related to fn would be destroyed. But the state related to other usages of the macro would not

A simple Code.toString()wouldn't be enough, as we'd want to support cases like:

@macro
void fn() {
  another();
}

void another() {
  // TODO change the source code of this function
}

We'd want a change in another to change the hash generated for fn. And of course, anothercould be defined in a separate library

It's a feature that I'm about to release in a package using build_runner, which massively improves the developer experience.
But I just realised that metaprogramming may not have what's necessary for that


Of course, there are possible alternative solutions. Such as having the SDK officially provide such an hash function
Then I guess function introspection could be private to the SDK.

I could see this pattern become quite popular due to the unique benefits it brings.

@tbuczkowski
Copy link

tbuczkowski commented Jul 24, 2024

I would find this feature useful too. My use case would be logging/analytics of specific method calls - so a macro would need to be able to inject some additional code into a function/method body, but stil preserve the original functionality, which as far as I understand is not possible with the current state of macros - you can only replace the function body entirely.

And yes, the same functionality could be achieved without macros with some changes to the project's code architecture and adding some boilerplate, but I think the goal of macros is removing exactly that kind of repeatable, cumbersome code.

@lrhn
Copy link
Member

lrhn commented Jul 24, 2024

Preserving the original behavior is possible using an augmented call.
(Used to be an augment super call, don't know where the implementation currently is).

So replace the body with

... {
  log("something or other");
  return augmented(args);
}

@tbuczkowski
Copy link

@lrhn I saw something like that in one of the macro examples, but couldn't get it to work (on the most recent version of Flutter's master channel), and also saw no mention of that anywhere in the documentation so I thought it could have been just an error in the example.

Are you aware if it's documented anywhere how I could use that mechanism in it's current state?

@jakemac53
Copy link
Contributor

I believe it just isn't implemented yet

@davidmorgan davidmorgan closed this as not planned Won't fix, can't repro, duplicate, stale Feb 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
static-metaprogramming Issues related to static metaprogramming
Projects
Development

No branches or pull requests

5 participants