Skip to content

breaking change: dart:mirrors check types of arguments passed to reflectively invoked methods  #35611

Closed
@mraleph

Description

@mraleph

Background

In Dart 2 and Dart 2.1 releases due to a bug in dart:mirrors implementation it was possible to violate strong mode type safety via dart:mirrors.

For example the following code:

import 'dart:mirrors';

class A {
  void method(int v) {
    if (v != null && v is! int) {
      print("This should be impossible: expected null or int got ${v}");
    }
  }
}

void main() {
  final obj = A();
  reflect(obj).invoke(#method, ['not-an-number']);
}

In Dart 2.1 release would print

This should be impossible: expected null or int got not-an-number

In Dart 2.1.1 we are addressing this issue meaning that this code would start throwing TypeError

Unhandled exception:
type 'String' is not a subtype of type 'int' of 'v'
#0      _TypeError._throwNew (dart:core/runtime/liberrors_patch.dart:89:51)
#1      _LocalInstanceMirror._invoke (dart:mirrors/runtime/libmirrors_impl.dart:327:37)
#2      _LocalInstanceMirror.invoke (dart:mirrors/runtime/libmirrors_impl.dart:323:25)
#3      main (file:///tmp/test.dart:13:16)
#4      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:289:19)
#5      _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)

Expected impact

We expect that only programs that are incorrect with respect to strong mode would break.

However this might also reveal issues where programs relying on mirrors were not taking care of creating instances of generic classes with correct type arguments - and instead relied on Dart 1 "dynamic-is-bottom" semantics.

For example the following code would stop working:

import 'dart:mirrors';

class B<T> {
}

class A {
  void method(B<int> v) {
  }
}

void main() {
  final obj = A();
  reflect(obj).invoke(#method, [B()]);
}

This would fail with

type 'B<dynamic>' is not a subtype of type 'B<int>' of 'v'

Addressing the breakage

You would need to change your reflective code to pass arguments of correct types, which also means creating instances with correct type arguments.

Note that if you need to construct object reflectively with the given type arguments you can use reflectType to create type mirror with the given type arguments and then cast this mirror to ClassMirror and use ClassMirror.newInstance:

print((reflectType(B, [int]) as ClassMirror).newInstance(const Symbol(""), []).reflectee);  // Instance of B<int>

Known Affected Packages

package:rpc see #35009

/cc @mit-mit

Metadata

Metadata

Assignees

Labels

area-vmUse area-vm for VM related issues, including code coverage, and the AOT and JIT backends.breaking-change-requestThis tracks requests for feedback on breaking changes

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions