Last night's exploration of annotations in Dart piqued my curiosity over how Angular.dart uses annotations so effectively.
To date, I have only used annotations as glorified code comments. But there is clearly great potential that I am overlooking. So I start with a simple
main.dart
file that contains a main entry point with an annotation:@meta('This is main!') main() { print('[main] Hello!'); }Simple enough, when run from the command-line, it prints out:
$ dart main.dart [main] Hello!It fails
dartanalyzer
analysis without defining that annotation:$ dartanalyzer main.dart Analyzing main.dart... [error] Annotation can be only constant variable or constant constructor invocation (/home/chris/repos/gists/reflecting_annotations/main.dart, line 5, col 1) 1 error found.So I define a simple “meta” annotation class:
/// Sweet meta data about a class (or thing) /// /// @meta('Nice code') /// class Bar { /* ... */} class meta { final String note; const meta(this.note); }That satisfies
dartanalyzer
while my code still runs as before.That's all well and good, but how do I get at that annotation data? For that, I am going to need to start with the
dart:mirrors
library:import 'dart:mirrors'; @meta('This is main!') main() { print('[main] Hello!'); }Now I need to do something with those mirrors. I have to confess that I find this a non-obvious path from this point. I know that I need to start by reflecting on
main()
: var selfMirror = reflect(main);
Reading through Angular.dart source, I know that I need to extract metadata, but it is not entirely clear to me how it does this. What is clear is that metadata
is a property of DeclarationMirror. But reflect()
is giving me a ClosureMirror
, which does not have a DeclarationMirror
property or method. After a bit of meandering, I find that ClosureMirror
has a function
property which just so happens to implement DeclarationMirror
.I can get that meta data (for the
@meta
annotation which I would have named differently had I known the name of the getter) as a list of InstanceMirror
objects. I access just the first and print it out:import 'dart:mirrors'; @meta('This is main!') main() { print('[main] Hello!'); var selfMirror = (reflect(main) as ClosureMirror).function; var metaData = selfMirror.metadata; print('[main] Meta is: ${metaData[0]}.'); }Which gives me:
$ dartanalyzer main.dart; dart main.dart Analyzing main.dart... No issues found [main] Hello! [main] Meta is: InstanceMirror on Instance of 'meta'.I try accessing the
note
field from my meta
class: print('[main] Meta is: ${metaData[0].getField(#note)}.');
That gets me the actual notation that I wanted, but I am still working with an object instead of the actual string note:[main] Meta is: InstanceMirror on "This is main!".To get the
note
property, I access the relfectee
property of the meta data (which is an InstanceMirror
):import 'dart:mirrors'; @meta('This is main!') main() { print('[main] Hello!'); var selfMirror = (reflect(main) as ClosureMirror).function; var metaData = selfMirror.metadata; print('[main] Meta is: ${metaData[0].reflectee.note}'); }That finally gets me access to the value that I wanted from the annotation:
$ dartanalyzer main.dart; dart main.dart Analyzing main.dart... No issues found [main] Hello! [main] Meta is: This is main!I probably have to work with this mirror stuff more to appreciate it. Right now, I am going from
ClosureMirror
to MethodMirror
to InstanceMirror
to “reflectee”—all of which seems like quite a journey. Thinking about it, I can understand the
InstanceMirror
/ “reflectee” relationship. The former has to hold any number of different types of objects (reflectees) in a consistent API, but also allow direct access to the actual object. I also get the
MethodMirror
/ InstanceMirror
relationship. The meta data is made available as a list of these instance mirrors, which it can build via the mirror class definition.I am not sure that I fully understand the difference between
ClosureMirror
and MethodMirror
. The latter, as a class that implements DeclarationMirror
, would seem to have more to do with the language. The ClosureMirror
, which implements InstanceMirror
would seem to have more to do with the running of the code. I suppose I begin to understand the difference, but it feels like there are some bigger concepts that I would only fully grasp with more experience. Which I may try to get more of tomorrow.Day #57
No comments:
Post a Comment