I know that the Dart reflection system is under heavy development and will likely change sometime early in the new year. Still, it seems far enough along that it is worth getting somewhat familiar with it.
Since it is likely to change, I will explore this library with some unit tests that can tell me when things change. I start with a simple unit test skeleton that also include my yummy
Cookie
class for use in exploration:import 'dart:mirrors'; import 'package:unittest/unittest.dart'; class Cookie { int number_of_chips; Cookie({this.number_of_chips:0}); } main() { group('[reflection]', (){ // tests will go here... }); }I start by using the dart:mirrors' top-level method
reflect()
to grab a instance mirror of my cookie class: group('[reflection]', (){
InstanceMirror im;
setUp((){
im = reflect(new Cookie(number_of_chips: 42));
});
});
At this point, I have reflected on an instance of the Cookie
class, which gives me a mirror with which to probe the instance. My first attempt at probing uses the getField
method from InstanceMirror
to see if I can grab the number of chips from the original instance: setUp((){
im = reflect(new Cookie(number_of_chips: 42));
});
test('getField', (){
expect(im.getField('number_of_chips'), equals(42));
});
That fails, telling me that instead of the expected value of 42
, I get a Future
instead:FAIL: [reflection] getField Expected: <42> but: was <Instance of '_FutureImpl@0x36924d72'>.That seems useful to note for the future, so I make a test to ensure that
getField
continues to return a Future
: test('getField returns a future', (){
var type = im.getField('number_of_chips').runtimeType;
expect(type.toString(), contains('Future'));
});
To extract the actual value out, I need to supply a then()
function for the Future
to invoke when it completes its thing: test('getField can find the original value', (){
im.getField('number_of_chips').then((v){
expect(v.reflectee, equals(42));
});
});
With that, I have two passing tests:➜ classes git:(master) ✗ dart reflection.dart unittest-suite-wait-for-done PASS: [reflection] getField returns a future PASS: [reflection] getField can find the original value All 2 tests passed.There is not much else to InstanceMirror aside from the
relfectee
property. Were I sending mirrors across isolates, I would expect some limitation in what I can do. Since I am writing my tests all in the same isolate, I should be able to access a Cookie
instance directly from the reflectee
property: test('reflectee in same isolate', (){
expect(im.hasReflectee, isTrue);
expect(im.reflectee.number_of_chips, equals(42));
});
And indeed that works:➜ classes git:(master) ✗ dart reflection.dart unittest-suite-wait-for-done PASS: [reflection] getField returns a future PASS: [reflection] getField can find the original value PASS: [reflection] reflectee in same isolate All 3 tests passed.I would expect that the
InstanceMirror
's reflectee
refers to the same object as the original rather than to a clone of the original. To be sure, I write a test, which requires a slight modification of the set()
code: InstanceMirror im;
Cookie cookie;
setUp((){
cookie = new Cookie(number_of_chips: 42);
im = reflect(cookie);
});
test('reflectee refers to the same object as the original', (){
expect(identical(cookie, im.reflectee), isTrue);
});
And it turns out that reflectees do point to the original instance:➜ classes git:(master) ✗ dart reflection.dart unittest-suite-wait-for-done PASS: [reflection] getField returns a future PASS: [reflection] getField can find the original value PASS: [reflection] reflectee in same isolate PASS: [reflection] reflectee refers to the same object as the original All 4 tests passed.Aside from
invoke()
, which seems to be identical to getField
except for methods instead of getters, that looks to be the extent of InstanceMirror
. Before calling it a night, I take a quick look at ClassMirror
. I can get a ClassMirror
instance from the type
property of my instance mirror.This lets me reflect on various aspects of the class, including the methods:
test('list instance methods', (){
var cm = im.type,
methods = cm.methods;
expect(methods, equals({}));
});
test('list instance getters', (){
var cm = im.type,
getters = cm.getters;
expect(getters, equals({}));
});
test('list instance members', (){
var cm = im.type,
members = cm.members;
expect(members.values.map((v)=>v.simpleName), equals(['number_of_chips']));
});
Since there are no getters or methods in my Cookie
class, I expect the class mirror to contain an empty map. There is the number_of_chips
property, which I am able to extract from the mirror with the members
property. With that, I have a fairly respectable start on understanding the current state of Dart mirrors:➜ classes git:(master) ✗ dart reflection.dart unittest-suite-wait-for-done PASS: [reflection] getField returns a future PASS: [reflection] getField can find the original value PASS: [reflection] reflectee in same isolate PASS: [reflection] reflectee refers to the same object as the original PASS: [reflection] list instance methods PASS: [reflection] list instance getters PASS: [reflection] list instance members All 7 tests passed.I will probably leave it at that for now, since mirrors in Dart are not baked yet:
➜ classes git:(master) ✗ dart_analyzer reflection.dart file:/home/chris/repos/csdart/Book/code/classes/reflection.dart:3:1: dart:mirrors is not fully implemented yet 2: 3: import 'dart:mirrors'; ~~~~~~~~~~~~~~~~~~~~~~I do look forward to more on this front. It seems like quite a bit of ceremony—especially coming from a Ruby/JavaScript background. Still, I am excited to see what comes of this library.
Day #616