Buckshot Videos

Loading...

Sunday, August 26, 2012

Working With Mirrors In Dart - A Brief Introduction

Mirrors provide a way to perform reflection on your application. The great thing about mirrors vs traditional reflection in other languages is that they are not intrinsically tied to the object system. This stratification provides many benefits. If you want to learn more about mirrors in general, check out this great list of links provided by Gilad Bracha.

Dragons! The mirror API is still very new and subject to change at any time. At the time of this writing, it is only supported in the Dart VM (not dart2js).

A mirror is a meta object, that provides information about, and in some cases operations on, objects in a Dart application. Each type of canonical "thing" in Dart has, generally speaking, a corresponding mirror type.

A listing of mirror types supported by the API.

As you can see from the list above, there is a mirror for just about everything. Different mirrors provide different kinds of visibility (introspection) and operations.

Mirrors Beget Mirrors

Mirror functions always return another mirror as a result. Even the instantiation and invocation functions will return an InstanceMirror. It is from the InstanceMirror that you can get access to the concrete object from your application.

Perusing Dart With Mirrors

The mirror API allows you to inspect pretty much anything in your application. At the top level, the starting point for doing so done this way:

final MirrorSystem ms = currentMirrorSystem();

I should note there that there is another sibling of currentMirrorSystem() called mirrorSystemOf(SendPort port), which is used to reflect on other isolates, but I won't be covering that in this post.

With this mirror in hand, you can iterate through all the libraries that are currently in scope of your application, iterating through the libraries map will naturally yield LibraryMirror's.

LibraryMirror, in turn, exposes collections of members, classes, functions, getters, setters, and variables for that particular library. It becomes trivial then, to drill down to exactly information you need.

Instantiating and Invoking

There are two mirror types in particular that provide a way to instantiate objects and invoke methods: ClassMirror and ObjectMirror. In ClassMirror, there is a function called newInstance() which will return a Future<InstanceMirror> object. Lets see how this works:

#import('dart:mirrors');
main(){
final foo = new Foo();
//lets get a reflection of foo
InstanceMirror fooIM = reflect(foo);
//now we get to the ClassMirror of fooIM
ClassMirror fooCM = fooIM.type;
//with our class mirror we can create a new instance of foo
//(an new InstanceMirror is returned)
fooCM
.newInstance('',[])
.then((InstanceMirror newIM){
//our concrete instance is now located in newIM.reflectee
newIM.reflectee.doSomething();
});
}
Class Foo
{
void doSomething(){
print('you did it!');
}
}

Running this program will yield:

you did it!

Invoking methods is not much more difficult, but there is one area where you will want to pay special attention: Invocation of instance methods will need to be done on an InstanceMirror, whereas invocation of static methods will be done on the ClassMirror. Lets try both:

.invoke() also returns a Future<InstanceMirror> on the result of the invocation, if we are interested in the return. In the example above we aren't.

Also in the example above, we pass a value to doSomething() during the invocation, and we need to wrap it in a mirror using reflect() (the Dart team may do this automatically for us in the future).

Non-final fields, along with explicit getters and setters, also have a similar invocation mechanism, but the calls are made to .getField() and .setField() instead. More information about these can be found in the ObjectMirror class.

Summary

The Dart mirrors API provides a powerful set of tools for reflecting on your code. Hopefully this brief introduction to the will help you get started.

Hi j23tom. I assume what you ask is "how do I get to a ClassMirror without having to reflect a concrete object?"

You do this by starting from a call to the currentMirrorSystem() function, which returns an object containing a map of all the libraries. You then query into the library for ClassMirror you are looking for (in the .classes map). From there you can query on fields (try the .variables map, for example).