Getting ClassMirror instances for all classes that have an annotation - dart

I have this annotation
class Target{
final String value;
const Target(this.value);
}
and 2 classes that are annotated with it
#Target("/313")
class c1{
}
#Target("/314")
class c2{
}
how can i get a List of ClassMirror instances for the classes that have the Target annotation?
based on the selected answer that is if i knew what library my calsses exist in
var mirrorSystem = currentMirrorSystem();
var libraryMirror = mirrorSystem.findLibrary(#testlib);
for(ClassMirror classMirror in libraryMirror.declarations.values){
if(classMirror.metadata!=null){
for(var meta in classMirror.metadata){
if(meta.type == reflectClass(Path)){
print(classMirror.simpleName);
print(meta.getField(#value));
}
}
}
}

This searches all libraries in the current isolate for classes that are annotated with #Target('/313')
#MirrorsUsed(metaTargets: Target) // might be necessary when you build this code to JavaScript
import 'dart:mirrors';
class Target {
final String id;
const Target(this.id);
}
#Target('/313')
class c1{
}
#Target('/314')
class c2{
}
#Target('/313')
#Target('/314')
class c3{
}
void main() {
MirrorSystem mirrorSystem = currentMirrorSystem();
mirrorSystem.libraries.forEach((lk, l) {
l.declarations.forEach((dk, d) {
if(d is ClassMirror) {
ClassMirror cm = d as ClassMirror;
cm.metadata.forEach((md) {
InstanceMirror metadata = md as InstanceMirror;
if(metadata.type == reflectClass(Target) && metadata.getField(#id).reflectee == '/313') {
print('found: ${cm.simpleName}');
}
});
}
});
});
}
found: Symbol("c3")
found: Symbol("c1")

Related

how to get type of identifier dart-analyzer

I am processing method statements in a class and i want to find type of an identifier in those statements.
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:dstore/dstore.dart';
import 'package:dstore_generator/src/utils.dart';
import 'package:source_gen/source_gen.dart';
AstNode getAstNodeFromElement(Element element) {
AnalysisSession session = element.session;
ParsedLibraryResult parsedLibResult =
session.getParsedLibraryByElement(element.library);
ElementDeclarationResult elDeclarationResult =
parsedLibResult.getElementDeclaration(element);
return elDeclarationResult.node;
}
class SelectorsGenerator extends GeneratorForAnnotation<Selectors> {
#override
String generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) {
if (!(element is ClassElement)) {
throw Exception("Selectors should be applied on class only");
}
final className = element.name;
if (!className.startsWith("_")) {
throw Exception("Selectors functions class should start with _");
}
final modelName = className.substring(1);
final visitor = SelectorsVisitor(modelName);
final astNode = getAstNodeFromElement(element);
astNode.visitChildren(visitor);
return """
// Selector
""";
}
}
class SelectorsVisitor extends SimpleAstVisitor {
final String modelName;
final selectors = <String>[];
SelectorsVisitor(this.modelName);
#override
dynamic visitMethodDeclaration(MethodDeclaration node) {
final fields = convertParamsToFields(node.parameters);
if (fields.isEmpty || fields.length > 1) {
throw Exception(
"Selector functions should be only one param with app state");
}
final field = fields.first;
var name = node.name.toString();
if (node.returnType == null) {
throw Exception("You sould annontate return type of method ${name} ");
}
final rType = node.returnType.toString();
final sType = field.type;
final bvs = SelectorBodyVisitor(field.param!.identifier);
node.body.visitChildren(bvs);
// node.body.visitChildren(visitor)
return super.visitMethodDeclaration(node);
}
}
class SelectorBodyVisitor extends RecursiveAstVisitor {
final Identifier identifier;
final List<List<String>> depsList = [];
SelectorBodyVisitor(this.identifier);
List<String> getListOfPropAccess(PropertyAccess node) {
final result = <String>[];
final prop = node.propertyName.toString();
result.add(prop);
final target = node.target;
print("target type ${target.runtimeType}");
if (target is PropertyAccess) {
result.addAll(getListOfPropAccess(target));
} else if (target is PrefixedIdentifier) {
if (target.prefix.toString() == identifier.toString()) {
// I am trying to get identifier type here
print("IdentifierElement2 ${target.identifier.staticType}");
result.add(target.identifier.toString());
} else {
print("target is not identifier ${target.runtimeType} ${target}");
}
}
return result;
}
#override
dynamic visitPropertyAccess(PropertyAccess node) {
print("***&&& propsAccess ${node}");
final list = getListOfPropAccess(node);
final sa = node.toString().split(".").toList();
if (sa.length - 1 == list.length) {
// property access of state identifier
depsList.add(list.reversed.toList().take(2).toList());
}
print("Property access list ++++=== ++++++ ${list}");
}
#override
dynamic visitPrefixedIdentifier(PrefixedIdentifier node) {
print(
"**##### IdenAccess ${node} id: ${node.identifier} prefix : ${node.prefix} mid :${identifier.toString()}");
if (node.prefix.toString() == identifier.toString()) {
print("IdentifierElement1 ${node.identifier.staticType}");
depsList.add([node.identifier.toString()]);
} else {
print("identifier is not equal ${node.prefix == identifier}");
}
}
}
Example
class User {
final String name;
User(this.name)
}
class Model {
final User user;
Model(this.user)
}
#Selectors()
abstract class MySelectors {
static s1(Model model) {
final n = model.user.name; // i want to know type of name in code generation time , please check this line print("IdentifierElement2 ${target.identifier.staticType}"); in above code , where i am getting null
}
}
Found answer , I have to use getResolvedLibraryByElement instead of getParsedLibraryByElement in getAstNodeFromElement method.
Future<AstNode> getAstNodeFromElement(Element element) async {
AnalysisSession session = element.session;
final s = await session.getResolvedLibraryByElement(element.library);
final s2 = s.getElementDeclaration(element);
return s2.node;
}

how to get annotations defined on a type from TypeAnnotation dart analyzer

I want to get TypeAnnotation source to get annotations defined on that type
// file1.dart
#Annoation(name :"hello")
class RType { }
// file2.dart
#Selectors()
class Example {
static Rtype hello() => null;
}
by using ast visitor i am able to get RType ( TypeAnnoation) , but i want to get actual RType and its annotations ..
class SelectorsGenerator extends GeneratorForAnnotation<Selectors> {
AstNode getAstNodeFromElement(Element element) {
AnalysisSession session = element.session;
ParsedLibraryResult parsedLibResult =
session.getParsedLibraryByElement(element.library);
ElementDeclarationResult elDeclarationResult =
parsedLibResult.getElementDeclaration(element);
return elDeclarationResult.node;
}
#override
generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) {
if (!(element is ClassElement)) {
throw Exception("Selectors should be applied on class only");
}
element = element as ClassElement;
final visitor = ExampleVisitor();
final astNode = getAstNodeFromElement(element);
astNode.visitChildren(visitor);
return """
// Selector
""";
}
}
class ExampleVisitor extends SimpleAstVisitor {
#override
visitMethodDeclaration(MethodDeclaration node) {
final t= node.returnType; //TypeAnnonation
t.type // DartType is null here :(
//TODO i want to get annotations defined on this type
}
}
You shouldn't need to switch to the AST model for this, it should be possible to get the annotation with the Element model.
var methods = classElement.methods;
for (var method in methods) {
var returnType = method.returnType;
var metadata = returnType.element.metadata;
// Do something with the annotation.
}

How to implement call caching (Memoization)

I want to implement call cache(memoization) in non-intrusive way with the metadata annotations.
Hopefully, it will work like this:
class A{
#Cached
foo(msg) {
return msg;
}
}
void main() {
#Cached
var foo = ()=>"hello";
}
Can it be achieved with only dart:mirrors ?
I wrote a whole blog post on this topic a while ago. It's too long to copy here, so here's the link:
http://dartery.blogspot.com/2012/09/memoizing-functions-in-dart.html
The upshot is that you can write higher-order memoizing functions, but they're limited in generality by Dart's lack of flexible args functions. Also, if you want to use dynamic programming with recursive functions, you need to write your function with memoization in mind - it needs to take itself as an argument, so you can pass in the memoized version.
My current solution allows:
class B {
#CachedCallName(#cachedBaz)
baz() => print("first call to baz");
}
class A extends B with CacheableCalls {
#CachedCallName(#foo)
_foo(msg) {
print("first call with: $msg");
return msg + msg;
}
}
void main() {
A a = new A();
print(a.foo(21));
print(a.foo(21));
a.cachedBaz();
print(a.foo(22));
a.cachedBaz();
}
Output:
first call with: 21
42
42
first call to baz
first call with: 22
44
Flaws:
- can't cache methods with their actual names.
- can extend collection view but can't cache existing operators like operator []
- can't cache functions.
Full source:
#MirrorsUsed(metaTargets: CachedCallName)
import 'dart:mirrors';
class CachedCallName {
final Symbol name;
const CachedCallName(this.name);
}
#proxy
class CacheableCalls {
Map _cache = new Map();
dynamic _chacheInvoke(InstanceMirror thisMirror, Symbol
methodName, Invocation invocation) {
String key = "$methodName${invocation.positionalArguments}"
"${invocation.namedArguments}";
if (_cache.containsKey(key)) {
return _cache[key];
} else {
InstanceMirror resultMirror = thisMirror.invoke(methodName,
invocation.positionalArguments, invocation.namedArguments);
_cache[key] = resultMirror.reflectee;
return resultMirror.reflectee;
}
}
dynamic noSuchMethod(Invocation invocation) {
bool isFound = false;
var result;
Symbol called = invocation.memberName;
InstanceMirror instanceMirror = reflect(this);
ClassMirror classMirror = instanceMirror.type;
classMirror.instanceMembers.forEach((Symbol name, MethodMirror mm) {
mm.metadata.forEach((InstanceMirror im) {
if (im.reflectee is CachedCallName) {
if (im.reflectee.name == called) {
isFound = true;
result = _chacheInvoke(instanceMirror, name, invocation);
}
}
});
});
if (isFound) {
return result;
} else {
throw new NoSuchMethodError(this, called,
invocation.positionalArguments, invocation.namedArguments);
}
}
}
class B {
#CachedCallName(#cachedBaz)
baz() => print("first call to baz");
}
class A extends B with CacheableCalls {
#CachedCallName(#foo)
_foo(msg) {
print("first call with: $msg");
return msg + msg;
}
}
void main() {
A a = new A();
print(a.foo(21));
print(a.foo(21));
a.cachedBaz();
print(a.foo(22));
a.cachedBaz();
}

Dart: How to convert variable identifier names to strings only for variables of a certain type

Using Dart here.
As the above title suggests, I have a class (shown below) that has three bool instance variables. What I want to do is create a function that inspects the identifier names of these instance variables and prints each of them out in a string. The .declarations getter that comes with the ClassMirror class ALMOST does this, except it also gives me the name of the Constructor and any other methods I have in the class. This is no good. So really what I want is a way to filter by type (i.e., only give me the boolean identifiers as strings.) Any way to do this?
class BooleanHolder {
bool isMarried = false;
bool isBoard2 = false;
bool isBoard3 = false;
List<bool> boolCollection;
BooleanHolder() {
}
void boolsToStrings() {
ClassMirror cm = reflectClass(BooleanHolder);
Map<Symbol, DeclarationMirror> map = cm.declarations;
for (DeclarationMirror dm in map.values) {
print(MirrorSystem.getName(dm.simpleName));
}
}
}
OUTPUT IS:
isMarried
isBoard2
isBoard3
boolsToStrings
BooleanHolder
Sample code.
import "dart:mirrors";
void main() {
var type = reflectType(Foo);
var found = filter(type, [reflectType(bool), reflectType(int)]);
for(var element in found) {
var name = MirrorSystem.getName(element.simpleName);
print(name);
}
}
List<VariableMirror> filter(TypeMirror owner, List<TypeMirror> types) {
var result = new List<VariableMirror>();
if (owner is ClassMirror) {
for (var declaration in owner.declarations.values) {
if (declaration is VariableMirror) {
var declaredType = declaration.type;
for (var type in types) {
if (declaredType.isSubtypeOf(type)) {
result.add(declaration);
}
}
}
}
}
return result;
}
class Foo {
bool bool1 = true;
bool bool2;
int int1;
int int2;
String string1;
String string2;
}
Output:
bool1
bool2
int1
int2

How to implement dynamic properties in Dart?

I would like to be able to back a dynamic property with a Map using a lookup in noSuchMethod(). However the latest changes makes the incoming property reference name unavailable. I can understand the minification scenario requiring us to use Symbols rather than Strings for names, but this makes implementing serializable dynamic properties difficult. Anyone have good ideas on how to approach this problem?
I can't use String names since the String names are not fixed between calls to the minifier. (This would completely break serialization)
You can access the original name with MirrorSystem.getName(symbol)
So a dynamic class could look like :
import 'dart:mirrors';
class A {
final _properties = new Map<String, Object>();
noSuchMethod(Invocation invocation) {
if (invocation.isAccessor) {
final realName = MirrorSystem.getName(invocation.memberName);
if (invocation.isSetter) {
// for setter realname looks like "prop=" so we remove the "="
final name = realName.substring(0, realName.length - 1);
_properties[name] = invocation.positionalArguments.first;
return;
} else {
return _properties[realName];
}
}
return super.noSuchMethod(invocation);
}
}
main() {
final a = new A();
a.i = 151;
print(a.i); // print 151
a.someMethod(); // throws
}
You could do something like this:
import 'dart:json' as json;
main() {
var t = new Thingy();
print(t.bob());
print(t.jim());
print(json.stringify(t));
}
class Thingy {
Thingy() {
_map[const Symbol('bob')] = "blah";
_map[const Symbol('jim')] = "oi";
}
final Map<Symbol, String> _map = new Map<Symbol, String>();
noSuchMethod(Invocation invocation) {
return _map[invocation.memberName];
}
toJson() => {
'bob': _map[const Symbol('bob')],
'jim': _map[const Symbol('jim')]};
}
Update - dynamic example:
import 'dart:json' as json;
main() {
var t = new Thingy();
t.add('bob', 'blah');
t.add('jim', 42);
print(t.bob());
print(t.jim());
print(json.stringify(t));
}
class Thingy {
final Map<Symbol, String> _keys = new Map<Symbol, String>();
final Map<Symbol, dynamic> _values = new Map<Symbol, dynamic>();
add(String key, dynamic value) {
_keys[new Symbol(key)] = key;
_values[new Symbol(key)] = value;
}
noSuchMethod(Invocation invocation) {
return _values[invocation.memberName];
}
toJson() {
var map = new Map<String, dynamic>();
_keys.forEach((symbol, name) => map[name] = _values[symbol]);
return map;
}
}
If you only need "dynamic properties", it should be enough to use Symbols as keys in the Map. If you also want to serialize that map, then you need to keep track of the original String names and use those for serialization. When deserializing, you'd have to create new Symbols from those Strings.
Note that all these scenarios (and basically everything that involves new Symbol) require a compiler to create a mapping of original names to the minified ones and put this mapping into the program, which of course makes it bigger.
Thanks for the solution of #Alexandre Ardhuin, I made some modification to make it runnable.
import 'dart:mirrors';
class object {
final _properties = new Map<String, Object>();
object();
object.from(Map<String, Object> initial) {
initial.entries.forEach((element) => _properties[element.key] = element.value);
}
noSuchMethod(Invocation invocation) {
if (invocation.isAccessor) {
final realName = MirrorSystem.getName(invocation.memberName);
if (invocation.isSetter) {
// for setter realname looks like "prop=" so we remove the "="
final name = realName.substring(0, realName.length - 1);
_properties[name] = invocation.positionalArguments.first;
return;
} else {
return _properties[realName];
}
}
return super.noSuchMethod(invocation);
}
#override
String toString() {
return _properties.toString();
}
}
main() {
// we can't use var or object type here, because analysis will consider
// https://dart.dev/tools/diagnostic-messages#undefined_setter
// The setter 'i' isn't defined for the type 'object'
// So dynamic is required here!
dynamic a = object.from({'a': 123, 'b': 234});
a.i = 151;
print(a); // print {a: 123, b: 234, i: 151}
try {
a.someMethod(); // throws NoSuchMethodError
} catch (e) {
print(e);
}
}

Resources