Sdk: ObjectMirror.invoke ignores typeArguments

Created on 23 Jun 2018  路  12Comments  路  Source: dart-lang/sdk

  • Dart SDK Version: 2.0.0-dev.64.1
  • Mac OSX Sierra
  • Browser: N/A

I'm updating the package json_god to work with Dart 2; it's a dart:mirrors-based reflection solution. The actual functionality works, but for the fact that Dart 2's generics are reified, and so to work with typed collections, I need to somehow attach type information to objects.

The solution I am attempting is to reflectively call List.cast with the necessary type argument; however, the ObjectMirror.delegate method forwards to ObjectMirror.invoke, which doesn't use the type arguments. Thus, the type arguments are ignored, and items like List<String> remain List<dynamic>.


My code:

logger.info('Casting list elements to ${typeArguments[0].reflectedType}');
var inv = new Invocation.genericMethod(#cast,
   [typeArguments[0].reflectedType], []);
logger.info('INVOCATION OF ${inv.memberName} with type args: ${inv
    .typeArguments}');
var output = reflect(it.toList()).delegate(inv);
logger.info('Casted list type: ${output.runtimeType}');

The current output:

[INFO] json_god: Casting list elements to String
[INFO] json_god: INVOCATION OF Symbol("cast") with type args: [String]
[INFO] json_god: About to deserialize es2015 to a String
[INFO] json_god: Value es2015 is a primitive
[INFO] json_god: About to deserialize stage-0 to a String
[INFO] json_god: Value stage-0 is a primitive
[INFO] json_god: Casted list type: CastList<dynamic, dynamic>

It's evident that that the type arguments are not actually being applied at all. I can't think of any possible workaround for this, unfortunately.

area-library library-mirrors

Most helpful comment

It would also be hugely helpful for ClassMirror.newInstance() to accept generic type arguments as well since I need to reflectively instantiate a class...

reflectClass(StreamController).newGenericInstance(Symbol(""), [<expectedType>], []);
// should yield instance of StreamController<expectedType>

All 12 comments

Is there anything else I can do about this? This bug means that anything that uses dart:mirrors for serialization, etc., will not be able to perform casts, and always produce runtime errors.

cc @rmacnak-google

FWIW, you really won't want to use .cast as part of a normal workflow. It creates a forwarding implementation of the underlying collection, and is mostly meant to be a temporary stop-gap. For more data sets, a defensive copy is probably better:

https://www.dartlang.org/guides/language/effective-dart/usage#avoid-using-cast

(Still doesn't help you with mirrors, of course)

Did you try List<T>.from ?

Thanks for the tip - I was able to resolve it by using List.from, which works perfectly.

I'd like to work on this, if no one else is planning on looking into it soon.

The solution I'm thinking of:

  1. Add an invokeGeneric method to ObjectMirror with signature:

    InstanceMirror invokeGeneric(
    Symbol memberName,
    List typeArguments,
    List positionalArguments,
    [Map namedArguments])

    (Analogous to Invocation having method and genericMethod factory methods.)

    Q: Should List<Type> be List<TypeMirror>? Is there a reason List and Map<Symbol, dynamic> aren't List<InstanceMirror> and Map<Symbol, InstanceMirror>, respectively?

  2. Change invoke and delegate to use this method instead.

  3. Plumb the extra parameter down through runtime/lib/mirrors.cc and into runtime/vm/object.cc. I see some TODOs by @crelier about "Support invocation of generic functions with type arguments," so I assume once I get that far down, then the rest of the code below is in place.

Does that sound like a reasonable plan? Or are there other aspects of dart:mirrors that should be updated for generics at the same time?

When generic functions got implemented in the VM, mirrors were supposed to be removed. Therefore, mirrors were only modified to gracefully ignore type arguments on functions without crashing. But no work was done to support them fully.

You can try to support generic functions in mirrors, but I think you will run into multiple issues. Mirrors would require a major revision for this to work properly. The author of mirrors, @rmacnak-google, can comment further.

@crelier Thanks for the background. To clarify, when you say mirrors were supposed to be removed, is that still the plan? Or has that changed?

Mirrors definitely survived much longer than expected. I am not clear about their future, though. @rmacnak-google may know.

It would also be hugely helpful for ClassMirror.newInstance() to accept generic type arguments as well since I need to reflectively instantiate a class...

reflectClass(StreamController).newGenericInstance(Symbol(""), [<expectedType>], []);
// should yield instance of StreamController<expectedType>

@KOGI That is already possible with

reflectType(StreamController, [<expected type>]).newInstance(Symbol(""), []);

@mdempsky I don't see mirrors being removed. The base language still isn't capable of abstracting over classes, libraries or messages.

@rmacnak-google thank you!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

emilniklas picture emilniklas  路  3Comments

jmesserly picture jmesserly  路  3Comments

DartBot picture DartBot  路  3Comments

Hixie picture Hixie  路  3Comments

gspencergoog picture gspencergoog  路  3Comments