Reapply "reflectType() dynamic type arguments support (#26012)"

This was a pull request: 8a8033a417

MirrorsUsed doesn't transitively include reflective information. However, it must still be able to create TypeMirrors for types that are used as return- or parameter types.

Initially, the patch checked that TypeMirrors had the correct number of arguments for generic types. This is now disabled.

A better approach would be to know if a class has full reflective information, or not. But this would require much bigger changes to the system.

R=sigmund@google.com

Review-Url: https://codereview.chromium.org/2615943004 .
This commit is contained in:
Florian Loitsch
2017-01-10 15:53:21 +01:00
parent a39234eede
commit 2547caab25
13 changed files with 669 additions and 461 deletions
+1 -1
View File
@@ -1984,7 +1984,7 @@ class JavaScriptBackend extends Backend {
/**
* Returns true if the element has to be resolved due to a mirrorsUsed
* annotation. If we have insufficient mirrors used annotations, we only
* keep additonal elements if treeshaking has been disabled.
* keep additional elements if treeshaking has been disabled.
*/
bool requiredByMirrorSystem(Element element) {
return hasInsufficientMirrorsUsed && isTreeShakingDisabled ||
+2 -2
View File
@@ -116,7 +116,7 @@ class MirrorUsageAnalyzerTask extends CompilerTask {
(librariesWithUsage != null && librariesWithUsage.contains(library));
}
/// Call-back from the resolver to analyze MirorsUsed annotations. The result
/// Call-back from the resolver to analyze MirrorsUsed annotations. The result
/// is stored in [analyzer] and later used to compute
/// [:analyzer.mergedMirrorUsage:].
void validate(NewExpression node, TreeElements mapping) {
@@ -260,7 +260,7 @@ class MirrorUsageAnalyzer {
return result;
}
/// Merge all [MirrorUsage] instances accross all libraries.
/// Merge all [MirrorUsage] instances across all libraries.
MirrorUsage mergeUsages(Map<LibraryElement, List<MirrorUsage>> usageMap) {
Set<MirrorUsage> usagesToMerge = new Set<MirrorUsage>();
usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) {
+64
View File
@@ -819,6 +819,70 @@ DEFINE_NATIVE_ENTRY(Mirrors_makeLocalTypeMirror, 1) {
}
DEFINE_NATIVE_ENTRY(Mirrors_instantiateGenericType, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, type, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(1));
ASSERT(type.HasResolvedTypeClass());
const Class& clz = Class::Handle(type.type_class());
if (!clz.IsGeneric()) {
const Array& error_args = Array::Handle(Array::New(3));
error_args.SetAt(0, type);
error_args.SetAt(1, String::Handle(String::New("key")));
error_args.SetAt(2, String::Handle(String::New(
"Type must be a generic class or function.")));
Exceptions::ThrowByType(Exceptions::kArgumentValue, error_args);
UNREACHABLE();
}
if (clz.NumTypeParameters() != args.Length()) {
const Array& error_args = Array::Handle(Array::New(3));
error_args.SetAt(0, args);
error_args.SetAt(1, String::Handle(String::New("typeArguments")));
error_args.SetAt(2, String::Handle(String::New(
"Number of type arguments does not match.")));
Exceptions::ThrowByType(Exceptions::kArgumentValue, error_args);
UNREACHABLE();
}
intptr_t num_expected_type_arguments = args.Length();
TypeArguments& type_args_obj = TypeArguments::Handle();
type_args_obj ^= TypeArguments::New(num_expected_type_arguments);
AbstractType& type_arg = AbstractType::Handle();
Instance& instance = Instance::Handle();
for (intptr_t i = 0; i < args.Length(); i++) {
instance ^= args.At(i);
if (!instance.IsType()) {
const Array& error_args = Array::Handle(Array::New(3));
error_args.SetAt(0, args);
error_args.SetAt(1, String::Handle(String::New("typeArguments")));
error_args.SetAt(2, String::Handle(String::New(
"Type arguments must be instances of Type.")));
Exceptions::ThrowByType(Exceptions::kArgumentValue, error_args);
UNREACHABLE();
}
type_arg ^= args.At(i);
type_args_obj.SetTypeAt(i, type_arg);
}
Type& instantiated_type =
Type::Handle(Type::New(clz, type_args_obj, TokenPosition::kNoSource));
instantiated_type ^= ClassFinalizer::FinalizeType(
clz, instantiated_type, ClassFinalizer::kCanonicalize);
if (instantiated_type.IsMalbounded()) {
const LanguageError& type_error =
LanguageError::Handle(instantiated_type.error());
const Array& error_args = Array::Handle(Array::New(3));
error_args.SetAt(0, args);
error_args.SetAt(1, String::Handle(String::New("typeArguments")));
error_args.SetAt(2, String::Handle(type_error.FormatMessage()));
Exceptions::ThrowByType(Exceptions::kArgumentValue, error_args);
UNREACHABLE();
}
return instantiated_type.raw();
}
DEFINE_NATIVE_ENTRY(Mirrors_mangleName, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
+14 -1
View File
@@ -1645,6 +1645,8 @@ class _Mirrors {
native "Mirrors_makeLocalClassMirror";
static TypeMirror makeLocalTypeMirror(Type key)
native "Mirrors_makeLocalTypeMirror";
static Type instantiateGenericType(Type key, typeArguments)
native "Mirrors_instantiateGenericType";
static Expando<ClassMirror> _declarationCache = new Expando("ClassMirror");
static Expando<TypeMirror> _instantiationCache = new Expando("TypeMirror");
@@ -1661,7 +1663,10 @@ class _Mirrors {
return classMirror;
}
static TypeMirror reflectType(Type key) {
static TypeMirror reflectType(Type key, [List<Type> typeArguments]) {
if (typeArguments != null) {
key = _instantiateType(key, typeArguments);
}
var typeMirror = _instantiationCache[key];
if (typeMirror == null) {
typeMirror = makeLocalTypeMirror(key);
@@ -1672,4 +1677,12 @@ class _Mirrors {
}
return typeMirror;
}
static Type _instantiateType(Type key, List<Type> typeArguments) {
if (typeArguments.isEmpty) {
throw new ArgumentError.value(
typeArguments, 'typeArguments', 'Type arguments list cannot be empty.');
}
return instantiateGenericType(key, typeArguments.toList(growable: false));
}
}
+2 -2
View File
@@ -33,8 +33,8 @@ import "dart:_internal" as internal;
return _Mirrors.reflectClass(key);
}
@patch TypeMirror reflectType(Type key) {
return _Mirrors.reflectType(key);
@patch TypeMirror reflectType(Type key, [List<Type> typeArguments]) {
return _Mirrors.reflectType(key, typeArguments);
}
@patch class MirrorSystem {
+1
View File
@@ -366,6 +366,7 @@ namespace dart {
V(Mirrors_evalInLibraryWithPrivateKey, 2) \
V(Mirrors_makeLocalClassMirror, 1) \
V(Mirrors_makeLocalTypeMirror, 1) \
V(Mirrors_instantiateGenericType, 2) \
V(Mirrors_mangleName, 2) \
V(MirrorReference_equals, 2) \
V(MirrorSystem_libraries, 0) \
File diff suppressed because it is too large Load Diff
@@ -43,9 +43,9 @@ ClassMirror reflectClass(Type key) {
}
@patch
TypeMirror reflectType(Type key) {
TypeMirror reflectType(Type key, [List<Type> typeArguments]) {
if (key == dynamic) {
return currentMirrorSystem().dynamicType;
}
return js.reflectType(key);
return js.reflectType(key, typeArguments);
}
+33 -28
View File
@@ -171,11 +171,16 @@ external ClassMirror reflectClass(Type key);
* If [key] is not an instance of [Type], then this function throws an
* [ArgumentError].
*
* Optionally takes a list of [typeArguments] for generic classes. If the list
* is provided, then the [key] must be a generic class type, and the number of
* the provided type arguments must be equal to the number of type variables
* declared by the class.
*
* Note that since one cannot obtain a [Type] object from another isolate, this
* function can only be used to obtain type mirrors on types of the current
* isolate.
*/
external TypeMirror reflectType(Type key);
external TypeMirror reflectType(Type key, [List<Type> typeArguments]);
/**
* A [Mirror] reflects some Dart language entity.
@@ -1229,7 +1234,7 @@ class Comment {
* see the comments for [symbols], [targets], [metaTargets] and [override].
*
* An import of `dart:mirrors` may have multiple [MirrorsUsed] annotations. This
* is particularly helpful to specify overrides for specific libraries. For
* is particularly helpful to specify overrides for specific libraries. For
* example:
*
* @MirrorsUsed(targets: 'foo.Bar', override: 'foo')
@@ -1241,7 +1246,7 @@ class Comment {
*/
class MirrorsUsed {
// Note: the fields of this class are untyped. This is because the most
// convenient way to specify symbols today is using a single string. In
// convenient way to specify symbols today is using a single string. In
// some cases, a const list of classes might be convenient. Some
// might prefer to use a const list of symbols.
@@ -1258,7 +1263,7 @@ class MirrorsUsed {
*
* Dart2js currently supports the following formats to specify symbols:
*
* * A constant [List] of [String] constants representing symbol names,
* * A constant [List] of [String] constants representing symbol names,
* e.g., `const ['foo', 'bar']`.
* * A single [String] constant whose value is a comma-separated list of
* symbol names, e.g., `"foo, bar"`.
@@ -1306,14 +1311,14 @@ class MirrorsUsed {
* 1. If the qualified name matches a library name, the matching library is
* the target.
* 2. Else, find the longest prefix of the name such that the prefix ends
* just before a `.` and is a library name.
* just before a `.` and is a library name.
* 3. Use that library as current scope. If no matching prefix was found, use
* the current library, i.e., the library where the [MirrorsUsed]
* the current library, i.e., the library where the [MirrorsUsed]
* annotation was placed.
* 4. Split the remaining suffix (the entire name if no library name was
* found in step 3) into a list of [String] using `.` as a
* found in step 3) into a list of [String] using `.` as a
* separator.
* 5. Select all targets in the current scope whose name matches a [String]
* 5. Select all targets in the current scope whose name matches a [String]
* from the list.
*
* For example:
@@ -1329,11 +1334,11 @@ class MirrorsUsed {
* @MirrorsUsed(targets: "my.library.one.A.aField")
* import "dart:mirrors";
*
* The [MirrorsUsed] annotation specifies `A` and `aField` from library
* The [MirrorsUsed] annotation specifies `A` and `aField` from library
* `my.library.one` as targets. This will mark the class `A` as a reflective
* target. The target specification for `aField` has no effect, as there is
* no target in `my.library.one` with that name.
*
* no target in `my.library.one` with that name.
*
* Note that everything within a target also is available for reflection.
* So, if a library is specified as target, all classes in that library
* become targets for reflection. Likewise, if a class is a target, all
@@ -1355,9 +1360,9 @@ class MirrorsUsed {
* effect. In particular, adding a library to [metaTargets] does not make
* the library's classes valid metadata annotations to enable reflection.
*
* If an instance of a class specified in [metaTargets] is used as
* If an instance of a class specified in [metaTargets] is used as
* metadata annotation on a library, class, field or method, that library,
* class, field or method is added to the set of targets for reflection.
* class, field or method is added to the set of targets for reflection.
*
* Example usage:
*
@@ -1377,10 +1382,10 @@ class MirrorsUsed {
* }
*
* In the above example. `reflectableMethod` is marked as reflectable by
* using the `Reflectable` class, which in turn is specified in the
* using the `Reflectable` class, which in turn is specified in the
* [metaTargets] annotation.
*
* The method `nonReflectableMethod` lacks a metadata annotation and thus
* The method `nonReflectableMethod` lacks a metadata annotation and thus
* will not be reflectable at runtime.
*/
final metaTargets;
@@ -1390,7 +1395,7 @@ class MirrorsUsed {
*
* When used as metadata on an import of "dart:mirrors", this metadata does
* not apply to the library in which the annotation is used, but instead
* applies to the other libraries (all libraries if "*" is used).
* applies to the other libraries (all libraries if "*" is used).
*
* The following text is non-normative:
*
@@ -1400,31 +1405,31 @@ class MirrorsUsed {
* libraries.
* * A single [String] constant whose value is a comma-separated list of
* library names.
*
* Conceptually, a [MirrorsUsed] annotation with [override] has the same
*
* Conceptually, a [MirrorsUsed] annotation with [override] has the same
* effect as placing the annotation directly on the import of `dart:mirrors`
* in each of the referenced libraries. Thus, if the library had no
* [MirrorsUsed] annotation before, its unconditional import of
* in each of the referenced libraries. Thus, if the library had no
* [MirrorsUsed] annotation before, its unconditional import of
* `dart:mirrors` is overridden by an annotated import.
*
*
* Note that, like multiple explicit [MirrorsUsed] annotations, using
* override on a library with an existing [MirrorsUsed] annotation is
* additive. That is, the overall set of reflective targets is the union
* of the reflective targets that arise from the original and the
* overriding [MirrorsUsed] annotations.
* overriding [MirrorsUsed] annotations.
*
* The use of [override] is only meaningful for libraries that have an
* The use of [override] is only meaningful for libraries that have an
* import of `dart:mirrors` without annotation because otherwise it would
* work exactly the same way without the [override] parameter.
*
* While the annotation will apply to the given target libraries, the
* [symbols], [targets] and [metaTargets] are still evaluated in the
* [symbols], [targets] and [metaTargets] are still evaluated in the
* scope of the annotation. Thus, to select a target from library `foo`,
* a qualified name has to be used or, if the target is visible in the
* current scope, its type may be referenced.
*
*
* For example, the following code marks all targets in the library `foo`
* as reflectable that have a metadata annotation using the `Reflectable`
* as reflectable that have a metadata annotation using the `Reflectable`
* class from the same library.
*
* @MirrorsUsed(metaTargets: "foo.Reflectable", override: "foo")
@@ -1438,8 +1443,8 @@ class MirrorsUsed {
final override;
/**
* See the documentation for [MirrorsUsed.symbols], [MirrorsUsed.targets],
* [MirrorsUsed.metaTargets] and [MirrorsUsed.override] for documentation
* See the documentation for [MirrorsUsed.symbols], [MirrorsUsed.targets],
* [MirrorsUsed.metaTargets] and [MirrorsUsed.override] for documentation
* of the parameters.
*/
const MirrorsUsed(
+12
View File
@@ -105,6 +105,18 @@ mirrors/variable_is_const_test/none: RuntimeError # Issue 14671
mirrors/raw_type_test/01: RuntimeError # Issue 6490
mirrors/mirrors_reader_test: Slow, RuntimeError # Issue 16589
mirrors/regress_26187_test: RuntimeError # Issue 6490
mirrors/reflected_type_generics_test/01: Fail # Issues in reflecting generic typedefs.
mirrors/reflected_type_generics_test/02: Fail # Issues in reflecting bounded type variables.
# The following tests fail because we have disabled a test in
# `reflectClassByName`. `MirrorsUsed` leads to classes not having the
# information necessary to correctly handle these checks.
mirrors/reflected_type_generics_test/03: Fail # Issues in reflecting generic typedefs.
mirrors/reflected_type_generics_test/04: Fail # Issues in reflecting bounded type variables.
mirrors/reflected_type_generics_test/05: Fail # Issues in reflecting generic typedefs.
mirrors/reflected_type_generics_test/06: Fail # Issues in reflecting bounded type variables.
[ $compiler == none && $unchecked ]
mirrors/reflected_type_generics_test/02: Fail, Ok # Type check for a bounded type argument.
[ $compiler == dart2js && $fast_startup ]
mirrors/*: Fail # mirrors not supported
@@ -0,0 +1,28 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library Test;
@MirrorsUsed(targets: const ["Test"])
import 'dart:mirrors';
import 'dart:async';
import 'package:expect/expect.dart';
class A {
// Because of the `mirrors-used` annotation, the types `List` and `Future`
// are not reflectable.
// However, we still need to be able to create a Mirror for them, when we
// create a mirror for `foo`. In particular, it must be able to create a
// mirror, even though there are generic types.
List<int> foo(Future<int> x) {
return null;
}
}
void main() {
var m = reflect(new A()).type.instanceMembers[#foo];
Expect.equals(#List, m.returnType.simpleName);
Expect.equals(#Future, m.parameters[0].type.simpleName);
}
@@ -0,0 +1,99 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library test.reflected_type_generics_test;
@MirrorsUsed(targets: "test.reflected_type_generics_test")
import 'dart:mirrors';
import 'package:expect/expect.dart';
import 'reflected_type_helper.dart';
class A<T> {}
class P {}
class B extends A<P> {}
class C<K, V> {}
class D<T> extends A<T> {}
class E<K> extends C<K, int> {}
class F<G> {}
typedef bool Predicate<T>(T arg);
class FBounded<S extends FBounded> {}
class Helper<T> {
Type get param => T;
}
class Mixin<T extends P> {}
class Composite<K extends P, V> extends Object with Mixin<K> {}
main() {
// "Happy" paths:
expectReflectedType(reflectType(A, [P]), new A<P>().runtimeType);
expectReflectedType(reflectType(C, [B, P]), new C<B, P>().runtimeType);
expectReflectedType(reflectType(D, [P]), new D<P>().runtimeType);
expectReflectedType(reflectType(E, [P]), new E<P>().runtimeType);
expectReflectedType(
reflectType(FBounded, [FBounded]), new FBounded<FBounded>().runtimeType);
var predicateHelper = new Helper<Predicate<P>>();
expectReflectedType(reflectType(Predicate, [P]), predicateHelper.param); /// 01: ok
var composite = new Composite<P, int>();
expectReflectedType(reflectType(Composite, [P, int]), composite.runtimeType);
// Edge cases:
Expect.throws(
() => reflectType(P, []),
(e) => e is ArgumentError && e.invalidValue is List,
"Should throw an ArgumentError if reflecting not a generic class with "
"empty list of type arguments");
Expect.throws( /// 03: ok
() => reflectType(P, [B]), /// 03: continued
(e) => e is Error, /// 03: continued
"Should throw an ArgumentError if reflecting not a generic class with " /// 03: continued
"some type arguments"); /// 03: continued
Expect.throws(
() => reflectType(A, []),
(e) => e is ArgumentError && e.invalidValue is List,
"Should throw an ArgumentError if type argument list is empty for a "
"generic class");
Expect.throws( /// 04: ok
() => reflectType(A, [P, B]), /// 04: continued
(e) => e is ArgumentError && e.invalidValue is List, /// 04: continued
"Should throw an ArgumentError if number of type arguments is not " /// 04: continued
"correct"); /// 04: continued
Expect.throws(() => reflectType(B, [P]), (e) => e is Error, /// 05: ok
"Should throw an ArgumentError for non-generic class extending " /// 05: continued
"generic one"); /// 05: continued
Expect.throws(
() => reflectType(A, ["non-type"]),
(e) => e is ArgumentError && e.invalidValue is List,
"Should throw an ArgumentError when any of type arguments is not a Type");
Expect.throws( /// 06: ok
() => reflectType(A, [P, B]), /// 06: continued
(e) => e is ArgumentError && e.invalidValue is List, /// 06: continued
"Should throw an ArgumentError if number of type arguments is not correct " /// 06: continued
"for generic extending another generic"); /// 06: continued
Expect.throws(
() => reflectType(reflectType(F).typeVariables[0].reflectedType, [int]));
Expect.throws(() => reflectType(FBounded, [int])); /// 02: ok
var boundedType =
reflectType(FBounded).typeVariables[0].upperBound.reflectedType;
Expect.throws(() => reflectType(boundedType, [int])); /// 02: ok
Expect.throws(() => reflectType(Composite, [int, int])); /// 02: ok
// Instantiation of a generic class preserves type information:
ClassMirror m = reflectType(A, [P]) as ClassMirror;
var instance = m.newInstance(const Symbol(""), []).reflectee;
Expect.equals(new A<P>().runtimeType, instance.runtimeType);
}
@@ -4,6 +4,7 @@
library test.reflected_type_helper;
@MirrorsUsed(targets: "test.reflected_type_helper")
import 'dart:mirrors';
import 'package:expect/expect.dart';