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);
}
}
Related
I've seen in polymer.dart they have:
class CustomTag {
final String tagName;
const CustomTag(this.tagName);
}
but how does that interact with the rest of the code? from just the code above I can't see how using #CustomTag('my-tag') actually does anything but creates a CustomTag which is then garbage collected since nothing is referencing it.
To answer the question in the title; these are called Annotations; they are simply const constructors.
To answer the second question; these are usually used for tooling (eg. #deprecated) or rewriting via a Transformer. You can access them at runtime using mirrors, but that's probably not practical/advisable for a production web app that gets converted to JavaScript.
Here's some sample code taken from this answer
import "dart:mirrors";
void main() {
var object = new Class1();
var classMirror = reflectClass(object.runtimeType);
// Retrieve 'HelloMetadata' for 'object'
HelloMetadata hello = getAnnotation(classMirror, HelloMetadata);
print("'HelloMetadata' for object: $hello");
// Retrieve 'Goodbye' for 'object.method'
var methodMirror = (reflect(object.method) as ClosureMirror).function;
Goodbye goodbye = getAnnotation(methodMirror, Goodbye);
print("'Goodbye' for object: $goodbye");
// Retrieve all 'Goodbye' for 'object.method'
List<Goodbye> goodbyes = getAnnotations(methodMirror, Goodbye);
print("'Goodbye's for object.method': $goodbyes");
// Retrieve all metadata for 'object.method'
List all = getAnnotations(methodMirror);
print("'Metadata for object.method': $all");
}
Object getAnnotation(DeclarationMirror declaration, Type annotation) {
for (var instance in declaration.metadata) {
if (instance.hasReflectee) {
var reflectee = instance.reflectee;
if (reflectee.runtimeType == annotation) {
return reflectee;
}
}
}
return null;
}
List getAnnotations(DeclarationMirror declaration, [Type annotation]) {
var result = [];
for (var instance in declaration.metadata) {
if (instance.hasReflectee) {
var reflectee = instance.reflectee;
if (annotation == null) {
result.add(reflectee);
} else if (reflectee.runtimeType == annotation) {
result.add(reflectee);
}
}
}
return result;
}
#HelloMetadata("Class1")
class Class1 {
#HelloMetadata("method")
#Goodbye("method")
#Goodbye("Class1")
void method() {
}
}
class HelloMetadata {
final String text;
const HelloMetadata(this.text);
String toString() => "Hello '$text'";
}
class Goodbye {
final String text;
const Goodbye(this.text);
String toString() => "Goodbye '$text'";
}
Output:
'HelloMetadata' for object: Hello 'Class1'
'Goodbye' for object: Goodbye 'method'
'Goodbye's for object.method': [Goodbye 'method', Goodbye 'Class1']
'Metadata for object.method': [Hello 'method', Goodbye 'method', Goodbye 'Class1']
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();
}
I was wondering if is possible to create an instance of a generic type in Dart. In other languages like Java you could work around this using reflection, but I'm not sure if this is possible in Dart.
I have this class:
class GenericController <T extends RequestHandler> {
void processRequest() {
T t = new T(); // ERROR
}
}
I tried mezonis approach with the Activator and it works. But it is an expensive approach as it uses mirrors, which requires you to use "mirrorsUsed" if you don't want to have a 2-4MB js file.
This morning I had the idea to use a generic typedef as generator and thus get rid of reflection:
You define a method type like this: (Add params if necessary)
typedef S ItemCreator<S>();
or even better:
typedef ItemCreator<S> = S Function();
Then in the class that needs to create the new instances:
class PagedListData<T>{
...
ItemCreator<T> creator;
PagedListData(ItemCreator<T> this.creator) {
}
void performMagic() {
T item = creator();
...
}
}
Then you can instantiate the PagedList like this:
PagedListData<UserListItem> users
= new PagedListData<UserListItem>(()=> new UserListItem());
You don't lose the advantage of using generic because at declaration time you need to provide the target class anyway, so defining the creator method doesn't hurt.
You can use similar code:
import "dart:mirrors";
void main() {
var controller = new GenericController<Foo>();
controller.processRequest();
}
class GenericController<T extends RequestHandler> {
void processRequest() {
//T t = new T();
T t = Activator.createInstance(T);
t.tellAboutHimself();
}
}
class Foo extends RequestHandler {
void tellAboutHimself() {
print("Hello, I am 'Foo'");
}
}
abstract class RequestHandler {
void tellAboutHimself();
}
class Activator {
static createInstance(Type type, [Symbol constructor, List
arguments, Map<Symbol, dynamic> namedArguments]) {
if (type == null) {
throw new ArgumentError("type: $type");
}
if (constructor == null) {
constructor = const Symbol("");
}
if (arguments == null) {
arguments = const [];
}
var typeMirror = reflectType(type);
if (typeMirror is ClassMirror) {
return typeMirror.newInstance(constructor, arguments,
namedArguments).reflectee;
} else {
throw new ArgumentError("Cannot create the instance of the type '$type'.");
}
}
}
I don't know if this is still useful to anyone. But I have found an easy workaround. In the function you want to initialize the type T, pass an extra argument of type T Function(). This function should return an instance of T. Now whenever you want to create object of T, call the function.
class foo<T> {
void foo(T Function() creator) {
final t = creator();
// use t
}
}
P.S. inspired by Patrick's answer
2022 answer
Just came across this problem and found out that although instantiating using T() is still not possible, you can get the constructor of an object easier with SomeClass.new in dart>=2.15.
So what you could do is:
class MyClass<T> {
final T Function() creator;
MyClass(this.creator);
T getGenericInstance() {
return creator();
}
}
and when using it:
final myClass = MyClass<SomeOtherClass>(SomeOtherClass.new)
Nothing different but looks cleaner imo.
Here's my work around for this sad limitation
class RequestHandler {
static final _constructors = {
RequestHandler: () => RequestHandler(),
RequestHandler2: () => RequestHandler2(),
};
static RequestHandler create(Type type) {
return _constructors[type]();
}
}
class RequestHandler2 extends RequestHandler {}
class GenericController<T extends RequestHandler> {
void processRequest() {
//T t = new T(); // ERROR
T t = RequestHandler.create(T);
}
}
test() {
final controller = GenericController<RequestHandler2>();
controller.processRequest();
}
Sorry but as far as I know, a type parameter cannot be used to name a constructor in an instance creation expression in Dart.
Working with FLutter
typedef S ItemCreator<S>();
mixin SharedExtension<T> {
T getSPData(ItemCreator<T> creator) async {
return creator();
}
}
Abc a = sharedObj.getSPData(()=> Abc());
P.S. inspired by Patrick
simple like that.
import 'dart:mirrors';
void main(List<String> args) {
final a = A<B>();
final b1 = a.getInstance();
final b2 = a.getInstance();
print('${b1.value}|${b1.text}|${b1.hashCode}');
print('${b2.value}|${b2.text}|${b2.hashCode}');
}
class A<T extends B> {
static int count = 0;
T getInstance() {
return reflectClass(T).newInstance(
Symbol(''),
['Text ${++count}'],
{Symbol('value'): count},
).reflectee;
}
}
class B {
final int value;
final String text;
B(this.text, {required this.value});
}
Inspired by Patrick's answer, this is the factory I ended up with.
class ServiceFactory<T> {
static final Map<Type, dynamic> _cache = <String, dynamic>{};
static T getInstance<T>(T Function() creator) {
String typeName = T.toString();
return _cache.putIfAbsent(typeName, () => creator());
}
}
Then I would use it like this.
final authClient = ServiceFactory.getInstance<AuthenticationClient>(() => AuthenticationClient());
Warning: Erik made a very good point in the comment below that the same type name can exist in multiple packages and that will cause issues. As much as I dislike to force the user to pass in a string key (that way it's the consumer's responsibility to ensuring the uniqueness of the type name), that might be the only way.
Say I have
class RestSimulator {
#Path("/var")
void functionOne() {
final Type type = this.runtimeType;
final InstanceMirror instanceMirror = reflect(this);
final ClassMirror classMirror = instanceMirror.type;
final MethodMirror methodMirror = ?????
var metadata = methodMirror.metadata;
var path = metadata.first.reflectee;
print(path.toString()):
}
}
How can I get the MethodMirror for the calling function???
[Update]
I meant without doing something like
final MethodMirror methodMirror = functions[const Symbol('functionOne')];
So probably the main question is: How do I get the Symbol for the calling / current function?
AFAIK there's no simple way to get a reference on the current function at runtime.
I say simple because you can get the name from a StackTrace but it's really ugly and has horrible performances...
class A {
m() {
var functionName;
try {
throw '';
} catch(e, s) {
functionName = parseStackTraceToGetMethod(s.toString());
}
print(functionName); // displays A.m
}
}
parseStackTraceToGetMethod(String s) =>
s.substring(8, s.indexOf("("));
main() {
new A().m();
}
I need to allow my content pipeline extension to use a pattern similar to a factory. I start with a dictionary type:
public delegate T Mapper<T>(MapFactory<T> mf, XElement d);
public class MapFactory<T>
{
Dictionary<string, Mapper<T>> map = new Dictionary<string, Mapper<T>>();
public void Add(string s, Mapper<T> m)
{
map.Add(s, m);
}
public T Get(XElement xe)
{
if (xe == null) throw new ArgumentNullException(
"Invalid document");
var key = xe.Name.ToString();
if (!map.ContainsKey(key)) throw new ArgumentException(
key + " is not a valid key.");
return map[key](this, xe);
}
public IEnumerable<T> GetAll(XElement xe)
{
if (xe == null) throw new ArgumentNullException(
"Invalid document");
foreach (var e in xe.Elements())
{
var val = e.Name.ToString();
if (map.ContainsKey(val))
yield return map[val](this, e);
}
}
}
Here is one type of object I want to store:
public partial class TestContent
{
// Test type
public string title;
// Once test if true
public bool once;
// Parameters
public Dictionary<string, object> args;
public TestContent()
{
title = string.Empty;
args = new Dictionary<string, object>();
}
public TestContent(XElement xe)
{
title = xe.Name.ToString();
args = new Dictionary<string, object>();
xe.ParseAttribute("once", once);
}
}
XElement.ParseAttribute is an extension method that works as one might expect. It returns a boolean that is true if successful.
The issue is that I have many different types of tests, each of which populates the object in a way unique to the specific test. The element name is the key to MapFactory's dictionary. This type of test, while atypical, illustrates my problem.
public class LogicTest : TestBase
{
string opkey;
List<TestBase> items;
public override bool Test(BehaviorArgs args)
{
if (items == null) return false;
if (items.Count == 0) return false;
bool result = items[0].Test(args);
for (int i = 1; i < items.Count; i++)
{
bool other = items[i].Test(args);
switch (opkey)
{
case "And":
result &= other;
if (!result) return false;
break;
case "Or":
result |= other;
if (result) return true;
break;
case "Xor":
result ^= other;
break;
case "Nand":
result = !(result & other);
break;
case "Nor":
result = !(result | other);
break;
default:
result = false;
break;
}
}
return result;
}
public static TestContent Build(MapFactory<TestContent> mf, XElement xe)
{
var result = new TestContent(xe);
string key = "Or";
xe.GetAttribute("op", key);
result.args.Add("key", key);
var names = mf.GetAll(xe).ToList();
if (names.Count() < 2) throw new ArgumentException(
"LogicTest requires at least two entries.");
result.args.Add("items", names);
return result;
}
}
My actual code is more involved as the factory has two dictionaries, one that turns an XElement into a content type to write and another used by the reader to create the actual game objects.
I need to build these factories in code because they map strings to delegates. I have a service that contains several of these factories. The mission is to make these factory classes available to a content processor. Neither the processor itself nor the context it uses as a parameter have any known hooks to attach an IServiceProvider or equivalent.
Any ideas?
I needed to create a data structure essentially on demand without access to the underlying classes as they came from a third party, in this case XNA Game Studio. There is only one way to do this I know of... statically.
public class TestMap : Dictionary<string, string>
{
private static readonly TestMap map = new TestMap();
private TestMap()
{
Add("Logic", "LogicProcessor");
Add("Sequence", "SequenceProcessor");
Add("Key", "KeyProcessor");
Add("KeyVector", "KeyVectorProcessor");
Add("Mouse", "MouseProcessor");
Add("Pad", "PadProcessor");
Add("PadVector", "PadVectorProcessor");
}
public static TestMap Map
{
get { return map; }
}
public IEnumerable<TestContent> Collect(XElement xe, ContentProcessorContext cpc)
{
foreach(var e in xe.Elements().Where(e => ContainsKey(e.Name.ToString())))
{
yield return cpc.Convert<XElement, TestContent>(
e, this[e.Name.ToString()]);
}
}
}
I took this a step further and created content processors for each type of TestBase:
/// <summary>
/// Turns an imported XElement into a TestContent used for a LogicTest
/// </summary>
[ContentProcessor(DisplayName = "LogicProcessor")]
public class LogicProcessor : ContentProcessor<XElement, TestContent>
{
public override TestContent Process(XElement input, ContentProcessorContext context)
{
var result = new TestContent(input);
string key = "Or";
input.GetAttribute("op", key);
result.args.Add("key", key);
var items = TestMap.Map.Collect(input, context);
if (items.Count() < 2) throw new ArgumentNullException(
"LogicProcessor requires at least two items.");
result.args.Add("items", items);
return result;
}
}
Any attempt to reference or access the class such as calling TestMap.Collect will generate the underlying static class if needed. I basically moved the code from LogicTest.Build to the processor. I also carry out any needed validation in the processor.
When I get to reading these classes I will have the ContentService to help.