Here's a start at exposing an API to address https://github.com/dart-lang/sdk/issues/31371.

There is no actual implementation here yet (that's your job :) ), but there is:

- An external method in dart:_internal, extractTypeArguments().
- Empty patch methods for that for the VM, dart2js, and DDC. These need to have implementations
  filled in.
- A "dart_internal" package to expose a subset of the API. It gives you:

    extractListTypeArgument()
    extractMapTypeArguments()

  We'll bring this into Google, but not publish it externally unless we find we really need to.
- A test for the behavior. It probably has bugs since I can't run it.

See: https://github.com/dart-lang/sdk/issues/31371
Change-Id: I7d9f9a3a36f8e8be106440375c80d584898c83cb
Reviewed-on: https://dart-review.googlesource.com/26467
Commit-Queue: Bob Nystrom <rnystrom@google.com>
Reviewed-by: Leaf Petersen <leafp@google.com>
Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
Reviewed-by: Vijay Menon <vsm@google.com>
This commit is contained in:
Bob Nystrom 2017-12-08 21:57:00 +00:00 committed by commit-bot@chromium.org
parent 73ecd4c7b0
commit 03c8767f73
14 changed files with 318 additions and 3 deletions

View file

@ -29,6 +29,7 @@ convert:third_party/pkg/convert/lib
crypto:third_party/pkg/crypto/lib
csslib:third_party/pkg/csslib/lib
dart2js_info:third_party/pkg/dart2js_info/lib
dart_internal:pkg/dart_internal/lib
dart_messages:pkg/dart_messages/lib
dart_style:third_party/pkg_tested/dart_style/lib
dartdoc:third_party/pkg/dartdoc/lib

26
pkg/dart_internal/LICENSE Normal file
View file

@ -0,0 +1,26 @@
Copyright 2017, the Dart project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,3 @@
analyzer:
strong-mode:
implicit-casts: false

View file

@ -0,0 +1,43 @@
// 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.
// The actual functionality exposed by this package is implemented in
// "dart:_internal" since it is specific to each platform's runtime
// implementation. This package exists as a shell to expose that internal API
// to outside code.
//
// Only this exact special file is allowed to import "dart:_internal" without
// causing a compile error.
import 'dart:_internal' as internal;
/// Given an [Iterable], invokes [extract], passing the [iterable]'s type
/// argument as the type argument to the generic function.
///
/// Example:
///
/// ```dart
/// Object iterable = <int>[];
/// print(extractIterableTypeArgument(iterable, <T>() => new Set<T>());
/// // Prints "Instance of 'Set<int>'".
/// ```
Object extractIterableTypeArgument(
Iterable iterable, Object Function<T>() extract) =>
internal.extractTypeArguments<Iterable>(iterable, extract);
/// Given a [Map], invokes [extract], passing the [map]'s key and value type
/// arguments as the type arguments to the generic function.
///
/// Example:
///
/// ```dart
/// class Two<A, B> {}
///
/// main() {
/// Object map = <String, int>{};
/// print(extractMapTypeArguments(map, <K, V>() => new Two<K, V>());
/// // Prints "Instance of 'Two<String, int>'".
/// }
/// ```
Object extractMapTypeArguments(Map map, Object Function<K, V>() extract) =>
internal.extractTypeArguments<Map>(map, extract);

View file

@ -0,0 +1,18 @@
name: dart_internal
version: 0.1.0-dev
author: "Dart Team <misc@dartlang.org>"
homepage: http://www.dartlang.org
description: >
This package is not intended for any external use. Basically, if you don't
personally know exactly what it's for, you're probably not the intended user.
It contains functionality to enable some internal Google code to transition
to strong mode before some anticipated language features are in place. In
particular, it provides an API to solve the problem: "Given an object some
generic type A, how do I construct an instance of generic type B with the
same type argument(s)?"
publish_to:
# This package is not intended to be used externally (or, at least, not yet).
none
environment:
sdk: ">=2.0.0 <2.0.0"

View file

@ -47,6 +47,6 @@ List/*<E>*/ makeFixedListUnmodifiable/*<E>*/(List/*<E>*/ fixedLengthList) {
return fixedLengthList;
}
// TODO(vsm): Make this an @patch.
Object extractTypeArguments<T>(T instance, Function f) =>
dart.extractTypeArguments<T>(instance, f);
@patch
Object extractTypeArguments<T>(T instance, Function extract) =>
dart.extractTypeArguments<T>(instance, extract);

View file

@ -24,6 +24,12 @@ List<T> makeListFixedLength<T>(List<T> growableList)
List<T> makeFixedListUnmodifiable<T>(List<T> fixedLengthList)
native "Internal_makeFixedListUnmodifiable";
@patch
Object extractTypeArguments<T>(T instance, Function extract) {
// TODO(31371): Implement this.
throw new UnimplementedError();
}
class VMLibraryHooks {
// Example: "dart:isolate _Timer._factory"
static var timerFactory;

View file

@ -45,3 +45,9 @@ List<T> makeListFixedLength<T>(List<T> growableList) {
List<T> makeFixedListUnmodifiable<T>(List<T> fixedLengthList) {
return JSArray.markUnmodifiableList(fixedLengthList);
}
@patch
Object extractTypeArguments<T>(T instance, Function extract) {
// TODO(31371): Implement this.
throw new UnimplementedError();
}

View file

@ -96,3 +96,56 @@ int parseHexByte(String source, int index) {
int digit2 = hexDigitValue(source.codeUnitAt(index + 1));
return digit1 * 16 + digit2 - (digit2 & 256);
}
/// Given an [instance] of some generic type [T], and [extract], a first-class
/// generic function that takes the same number of type parameters as [T],
/// invokes the function with the same type arguments that were passed to T
/// when [instance] was constructed.
///
/// Example:
///
/// ```dart
/// class Two<A, B> {}
///
/// print(extractTypeArguments<List>(<int>[], <T>() => new Set<T>()));
/// // Prints: Instance of 'Set<int>'.
///
/// print(extractTypeArguments<Map>(<String, bool>{},
/// <T, S>() => new Two<T, S>));
/// // Prints: Instance of 'Two<String, bool>'.
/// ```
///
/// The type argument T is important to choose which specific type parameter
/// list in [instance]'s type hierarchy is being extracted. Consider:
///
/// ```dart
/// class A<T> {}
/// class B<T> {}
///
/// class C implements A<int>, B<String> {}
///
/// main() {
/// var c = new C();
/// print(extractTypeArguments<A>(c, <T>() => <T>[]));
/// // Prints: Instance of 'List<int>'.
///
/// print(extractTypeArguments<B>(c, <T>() => <T>[]));
/// // Prints: Instance of 'List<String>'.
/// }
/// ```
///
/// A caller must not:
///
/// * Pass `null` for [instance].
/// * Use a non-class type (i.e. a function type) for [T].
/// * Use a non-generic type for [T].
/// * Pass an instance of a generic type and a function that don't both take
/// the same number of type arguments:
///
/// ```dart
/// extractTypeArguments<List>(<int>[], <T, S>() => null);
/// ```
///
/// See this issue for more context:
/// https://github.com/dart-lang/sdk/issues/31371
external Object extractTypeArguments<T>(T instance, Function extract);

View file

@ -0,0 +1,155 @@
// 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.
/// Tests the (probably temporary) API for extracting reified type arguments
/// from an object.
import "package:expect/expect.dart";
// It's weird that a language test is testing code defined in a package. The
// rationale for putting this test here is:
//
// * This package is special and "built-in" to Dart in that the various
// compilers give it the special privilege of importing "dart:_internal"
// without error.
//
// * Eventually, the API being tested here may be replaced with an actual
// language feature, in which case this test will become an actual language
// test.
//
// * Placing the test here ensures it is tested on all of the various platforms
// and configurations where we need the API to work.
import "package:dart_internal/extract_type_arguments.dart";
main() {
testExtractIterableTypeArgument();
testExtractMapTypeArguments();
}
testExtractIterableTypeArgument() {
Object object = <int>[];
// Invokes function with iterable's type argument.
var called = false;
extractIterableTypeArgument(object, <T>() {
Expect.equals(T, int);
called = true;
});
Expect.isTrue(called);
// Returns result of function.
Object result = extractIterableTypeArgument(object, <T>() => new Set<T>());
Expect.isTrue(result is Set<int>);
Expect.isFalse(result is Set<bool>);
// Accepts user-defined implementations of Iterable.
object = new CustomIterable();
result = extractIterableTypeArgument(object, <T>() => new Set<T>());
Expect.isTrue(result is Set<String>);
Expect.isFalse(result is Set<bool>);
}
testExtractMapTypeArguments() {
Object object = <String, int>{};
// Invokes function with map's type arguments.
var called = false;
extractMapTypeArguments(object, <K, V>() {
Expect.equals(K, String);
Expect.equals(V, int);
called = true;
});
Expect.isTrue(called);
// Returns result of function.
Object result = extractMapTypeArguments(object, <K, V>() => new Two<K, V>());
Expect.isTrue(result is Two<String, int>);
Expect.isFalse(result is Two<int, String>);
// Accepts user-defined implementations of Map.
object = new CustomMap();
result = extractMapTypeArguments(object, <K, V>() => new Two<K, V>());
Expect.isTrue(result is Two<int, bool>);
Expect.isFalse(result is Two<bool, int>);
// Uses the type parameter order of Map, not any other type in the hierarchy.
object = new FlippedMap<double, Null>();
result = extractMapTypeArguments(object, <K, V>() => new Two<K, V>());
// Order is reversed here:
Expect.isTrue(result is Two<Null, double>);
Expect.isFalse(result is Two<double, Null>);
}
class Two<A, B> {}
// Implementing Iterable from scratch is kind of a chore, but ensures the API
// works even if the class never bottoms out on a concrete class defining in a
// "dart:" library.
class CustomIterable implements Iterable<String> {
bool any(Function test) => throw new UnimplementedError();
bool contains(Object element) => throw new UnimplementedError();
String elementAt(int index) => throw new UnimplementedError();
bool every(Function test) => throw new UnimplementedError();
Iterable<T> expand<T>(Function f) => throw new UnimplementedError();
String get first => throw new UnimplementedError();
String firstWhere(Function test, {Function orElse}) =>
throw new UnimplementedError();
T fold<T>(T initialValue, Function combine) => throw new UnimplementedError();
void forEach(Function f) => throw new UnimplementedError();
bool get isEmpty => throw new UnimplementedError();
bool get isNotEmpty => throw new UnimplementedError();
Iterator<String> get iterator => throw new UnimplementedError();
String join([String separator = ""]) => throw new UnimplementedError();
String get last => throw new UnimplementedError();
String lastWhere(Function test, {Function orElse}) =>
throw new UnimplementedError();
int get length => throw new UnimplementedError();
Iterable<T> map<T>(Function f) => throw new UnimplementedError();
String reduce(Function combine) => throw new UnimplementedError();
String get single => throw new UnimplementedError();
String singleWhere(Function test) => throw new UnimplementedError();
Iterable<String> skip(int count) => throw new UnimplementedError();
Iterable<String> skipWhile(Function test) => throw new UnimplementedError();
Iterable<String> take(int count) => throw new UnimplementedError();
Iterable<String> takeWhile(Function test) => throw new UnimplementedError();
List<String> toList({bool growable: true}) => throw new UnimplementedError();
Set<String> toSet() => throw new UnimplementedError();
Iterable<String> where(Function test) => throw new UnimplementedError();
}
class CustomMap implements Map<int, bool> {
bool operator [](Object key) => throw new UnimplementedError();
void operator []=(int key, bool value) => throw new UnimplementedError();
void addAll(Map<int, bool> other) => throw new UnimplementedError();
void clear() => throw new UnimplementedError();
bool containsKey(Object key) => throw new UnimplementedError();
bool containsValue(Object value) => throw new UnimplementedError();
void forEach(Function f) => throw new UnimplementedError();
bool get isEmpty => throw new UnimplementedError();
bool get isNotEmpty => throw new UnimplementedError();
Iterable<int> get keys => throw new UnimplementedError();
int get length => throw new UnimplementedError();
bool putIfAbsent(int key, Function ifAbsent) =>
throw new UnimplementedError();
bool remove(Object key) => throw new UnimplementedError();
Iterable<bool> get values => throw new UnimplementedError();
}
// Note: Flips order of type parameters.
class FlippedMap<V, K> implements Map<K, V> {
V operator [](Object key) => throw new UnimplementedError();
void operator []=(K key, V value) => throw new UnimplementedError();
void addAll(Map<K, V> other) => throw new UnimplementedError();
void clear() => throw new UnimplementedError();
bool containsKey(Object key) => throw new UnimplementedError();
bool containsValue(Object value) => throw new UnimplementedError();
void forEach(Function f) => throw new UnimplementedError();
bool get isEmpty => throw new UnimplementedError();
bool get isNotEmpty => throw new UnimplementedError();
Iterable<K> get keys => throw new UnimplementedError();
int get length => throw new UnimplementedError();
V putIfAbsent(K key, Function ifAbsent) => throw new UnimplementedError();
V remove(Object key) => throw new UnimplementedError();
Iterable<V> get values => throw new UnimplementedError();
}

View file

@ -142,6 +142,7 @@ error_stacktrace_test/00: MissingCompileTimeError
example_constructor_test: RuntimeError
external_test/21: CompileTimeError
external_test/24: CompileTimeError
extract_type_arguments_test: CompileTimeError # Issue 31371
f_bounded_quantification_test/01: MissingCompileTimeError
f_bounded_quantification_test/02: MissingCompileTimeError
factory1_test/00: MissingCompileTimeError

View file

@ -286,6 +286,7 @@ emit_const_fields_test: CompileTimeError # Issue 31533
export_ambiguous_main_test: MissingCompileTimeError
external_test/21: CompileTimeError
external_test/24: CompileTimeError
extract_type_arguments_test: CompileTimeError # Issue 31371
f_bounded_quantification_test/01: MissingCompileTimeError
f_bounded_quantification_test/02: MissingCompileTimeError
factory2_test/03: MissingCompileTimeError

View file

@ -473,6 +473,7 @@ example_constructor_test: Fail, OK
external_test/10: MissingRuntimeError # KernelVM bug: Unbound external.
external_test/13: MissingRuntimeError # KernelVM bug: Unbound external.
external_test/20: MissingRuntimeError # KernelVM bug: Unbound external.
extract_type_arguments_test: CompileTimeError # Issue 31371
f_bounded_quantification_test/01: MissingCompileTimeError
f_bounded_quantification_test/02: MissingCompileTimeError
factory2_test/03: MissingCompileTimeError

View file

@ -311,6 +311,7 @@ empty_block_case_test: MissingCompileTimeError
enum_private_test/02: MissingCompileTimeError
error_stacktrace_test/00: MissingCompileTimeError
export_ambiguous_main_test: MissingCompileTimeError
extract_type_arguments_test: CompileTimeError # Issue 31371
f_bounded_quantification_test/01: MissingCompileTimeError
f_bounded_quantification_test/02: MissingCompileTimeError
factory1_test/00: MissingCompileTimeError