google / reflectable.dart

Reflectable is a Dart library that allows programmers to eliminate certain usages of dynamic reflection by specialization of reflective code to an equivalent implementation using only static techniques. The use of dynamic reflection is constrained in order to ensure that the specialized code can be generated and will have a reasonable size.

Home Page:https://pub.dev/packages/reflectable

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question] - Listening to Method invocation

dinbtechit opened this issue · comments

is it possible to listen to the "Method" invocation?

Below is a pseudo-code that I am trying to accomplish.

In my client code, I have a Flutter Widget. Every time when the widget is removed from the tree the dispose() method is called. But I want to do some additional logic when the dispose() method is called (For example - I want to automatically unsubscribe any subscription and/or dispose of any controllers that are used in the widget.). Is this possible to accomplish this using Reflectable?

My Library Code

 var myWidget =  MyWidget();
 var instanceMirror = reflector.reflect(myWidget);

 instanceMirror.callBackWhenMethodInvoked(
       methodName: 'dispose', 
       function: callBackWhenMethodIsDisposed
);

/// Function is invoked when the dispose function is Called.
void callBackWhenMethodIsDisposed(ClassMirror classMirror) {
  // Do something when the function is disposed.
}

Client Code:

@Component()
class MyWidget extends StatefulWidget {

 @override
  State<MyWidget> createState() => _WidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  @override
   void dispose() {                 <-------<< // is it possible to know when this method is invoked?

   }
}

What you're looking for intercession, that is, a reflective mechanism that changes the semantics of an existing element in a given program. The reflection support offered by reflectable could never do that (because it relies on generated code, and no Dart code can do it), and 'dart:mirrors' also won't do it. They both support introspection on the target program, not modification.

Some language do support intercession (e.g., CLOS), but it is very tricky to make this work in a type safe manner. Some compile-time approaches exist as well, e.g., aspect orientation and in particular AspectJ. The compile-time approaches can be type checked, but it is then required that all usages of intercession are known at compile-time. Anyway, Dart doesn't have a mechanism like this.

So if you want to ensure that a particular method invocation has a certain "extra" effect then you need to control the creation of the object, such that you can ensure that it's an instance of a class that has this behavior.

So if you're doing var myWidget = MyWidget(); then you could as well do

mixin InterceptDispose implements MyWidget {
  void dispose() {
    ... // Whatever you want to do when `dispose` is called.
    super.dispose();
  }
}

class MyWidgetInterceptDispose = MyWidget with InterceptDispose;

var myWidget = MyWidgetInterceptDispose();

If you have no control over the creation of the widget then it's going to be harder. You might be able to create a wrapper class that forwards to the MyWidget for all methods except dispose (which would be like the dispose above, but calling theMyWidget.dispose() rather than super.dispose()).

Thank you so much for the top-class explanation! 🙌

What you want can be achieved through a proxy class, that will wrap the target instance.

Maybe you can do it with the package reflection_factory:
https://pub.dev/packages/reflection_factory#classproxy

I don't know if the package reflectable supports proxy class generation.

@gmpassos - That looks interesting!!! Thank you for sending along that package. Just curious will I be able to proxy a "Flutter Widget" that is not part of my library package? I have access to the instance of the widget and blue print of the class.

@ClassProxy('MimeTypeResolver')    <------<< // Is it possible to dynamically pass the class I want to proxy? 
                                            // Technically this class would be part of the user code. I will only have access to it at runtime
class MimeTypeResolverProxy implements ClassProxyListener {
  
  @override
  Object? onCall(instance, String methodName, Map<String, dynamic> parameters,
          TypeReflection? returnType) {
    var call = '$instance -> $methodName( $parameters ) -> $returnType';
    calls.add(call);
    print('CALL>> $call');
    return call;
  }

}

@eernstg - Do you know if reflectable can support proxy class generation?

There is also a concept of proxy in Reflectable -> proxy_test.dart as well. But I am assuming it's probably different than what reflection_factory offers.

Yes, you can generate code for 3rd part classes.

If needed you can open an issue at:

https://github.com/gmpassos/reflection_factory/issues

[Can reflectable] support proxy class generation?

proxy_test.dart was mentioned already, and that's a good illustration of one approach to proxying using reflectable:

The declaration of class Proxy implements A with a noSuchMethod creates a class with type A (so a Proxy can go anywhere an A can go), and that class has every member of A (generated by the compiler). The function createMethodMap creates a map which is used in the noSuchMethod implementation. You could create a map that handles every member (methods, getters, setters), and gives special treatment to dispose (or you could decide at run time which methods to give special treatment). This is a very flexible approach, but you will pay something for that in terms of run-time performance.

I do not know the package reflection_factory, but it looks like that package has dedicated support for proxy class creation via @ClassProxy('SomeClass', 'package:some_package/....'). This might fit your needs more directly (but, of course, @gmpassos can tell you what you can do and how to do it).