diff --git a/.packages b/.packages index a2dd24e2d8a..d559cca185e 100644 --- a/.packages +++ b/.packages @@ -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 diff --git a/pkg/dart_internal/LICENSE b/pkg/dart_internal/LICENSE new file mode 100644 index 00000000000..389ce985634 --- /dev/null +++ b/pkg/dart_internal/LICENSE @@ -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. diff --git a/pkg/dart_internal/analysis_options.yaml b/pkg/dart_internal/analysis_options.yaml new file mode 100644 index 00000000000..1a46de2ee05 --- /dev/null +++ b/pkg/dart_internal/analysis_options.yaml @@ -0,0 +1,3 @@ +analyzer: + strong-mode: + implicit-casts: false diff --git a/pkg/dart_internal/lib/extract_type_arguments.dart b/pkg/dart_internal/lib/extract_type_arguments.dart new file mode 100644 index 00000000000..7c93e5ff971 --- /dev/null +++ b/pkg/dart_internal/lib/extract_type_arguments.dart @@ -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 = []; +/// print(extractIterableTypeArgument(iterable, () => new Set()); +/// // Prints "Instance of 'Set'". +/// ``` +Object extractIterableTypeArgument( + Iterable iterable, Object Function() extract) => + internal.extractTypeArguments(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 {} +/// +/// main() { +/// Object map = {}; +/// print(extractMapTypeArguments(map, () => new Two()); +/// // Prints "Instance of 'Two'". +/// } +/// ``` +Object extractMapTypeArguments(Map map, Object Function() extract) => + internal.extractTypeArguments(map, extract); diff --git a/pkg/dart_internal/pubspec.yaml b/pkg/dart_internal/pubspec.yaml new file mode 100644 index 00000000000..49cb8dace2d --- /dev/null +++ b/pkg/dart_internal/pubspec.yaml @@ -0,0 +1,18 @@ +name: dart_internal +version: 0.1.0-dev +author: "Dart Team " +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" diff --git a/pkg/dev_compiler/tool/input_sdk/patch/internal_patch.dart b/pkg/dev_compiler/tool/input_sdk/patch/internal_patch.dart index d2c6235dfc5..221b62060ab 100644 --- a/pkg/dev_compiler/tool/input_sdk/patch/internal_patch.dart +++ b/pkg/dev_compiler/tool/input_sdk/patch/internal_patch.dart @@ -47,6 +47,6 @@ List/**/ makeFixedListUnmodifiable/**/(List/**/ fixedLengthList) { return fixedLengthList; } -// TODO(vsm): Make this an @patch. -Object extractTypeArguments(T instance, Function f) => - dart.extractTypeArguments(instance, f); +@patch +Object extractTypeArguments(T instance, Function extract) => + dart.extractTypeArguments(instance, extract); diff --git a/runtime/lib/internal_patch.dart b/runtime/lib/internal_patch.dart index 34f55c47a0d..f6bc9813fc4 100644 --- a/runtime/lib/internal_patch.dart +++ b/runtime/lib/internal_patch.dart @@ -24,6 +24,12 @@ List makeListFixedLength(List growableList) List makeFixedListUnmodifiable(List fixedLengthList) native "Internal_makeFixedListUnmodifiable"; +@patch +Object extractTypeArguments(T instance, Function extract) { + // TODO(31371): Implement this. + throw new UnimplementedError(); +} + class VMLibraryHooks { // Example: "dart:isolate _Timer._factory" static var timerFactory; diff --git a/sdk/lib/_internal/js_runtime/lib/internal_patch.dart b/sdk/lib/_internal/js_runtime/lib/internal_patch.dart index 144a54d0e70..05a9d39b659 100644 --- a/sdk/lib/_internal/js_runtime/lib/internal_patch.dart +++ b/sdk/lib/_internal/js_runtime/lib/internal_patch.dart @@ -45,3 +45,9 @@ List makeListFixedLength(List growableList) { List makeFixedListUnmodifiable(List fixedLengthList) { return JSArray.markUnmodifiableList(fixedLengthList); } + +@patch +Object extractTypeArguments(T instance, Function extract) { + // TODO(31371): Implement this. + throw new UnimplementedError(); +} diff --git a/sdk/lib/internal/internal.dart b/sdk/lib/internal/internal.dart index 4f47c5c9662..eeb0f852c20 100644 --- a/sdk/lib/internal/internal.dart +++ b/sdk/lib/internal/internal.dart @@ -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 {} +/// +/// print(extractTypeArguments([], () => new Set())); +/// // Prints: Instance of 'Set'. +/// +/// print(extractTypeArguments({}, +/// () => new Two)); +/// // Prints: Instance of 'Two'. +/// ``` +/// +/// 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 {} +/// class B {} +/// +/// class C implements A, B {} +/// +/// main() { +/// var c = new C(); +/// print(extractTypeArguments(c, () => [])); +/// // Prints: Instance of 'List'. +/// +/// print(extractTypeArguments(c, () => [])); +/// // Prints: Instance of 'List'. +/// } +/// ``` +/// +/// 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([], () => null); +/// ``` +/// +/// See this issue for more context: +/// https://github.com/dart-lang/sdk/issues/31371 +external Object extractTypeArguments(T instance, Function extract); diff --git a/tests/language_2/extract_type_arguments_test.dart b/tests/language_2/extract_type_arguments_test.dart new file mode 100644 index 00000000000..e63bbf3aa00 --- /dev/null +++ b/tests/language_2/extract_type_arguments_test.dart @@ -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 = []; + + // Invokes function with iterable's type argument. + var called = false; + extractIterableTypeArgument(object, () { + Expect.equals(T, int); + called = true; + }); + Expect.isTrue(called); + + // Returns result of function. + Object result = extractIterableTypeArgument(object, () => new Set()); + Expect.isTrue(result is Set); + Expect.isFalse(result is Set); + + // Accepts user-defined implementations of Iterable. + object = new CustomIterable(); + result = extractIterableTypeArgument(object, () => new Set()); + Expect.isTrue(result is Set); + Expect.isFalse(result is Set); +} + +testExtractMapTypeArguments() { + Object object = {}; + + // Invokes function with map's type arguments. + var called = false; + extractMapTypeArguments(object, () { + Expect.equals(K, String); + Expect.equals(V, int); + called = true; + }); + Expect.isTrue(called); + + // Returns result of function. + Object result = extractMapTypeArguments(object, () => new Two()); + Expect.isTrue(result is Two); + Expect.isFalse(result is Two); + + // Accepts user-defined implementations of Map. + object = new CustomMap(); + result = extractMapTypeArguments(object, () => new Two()); + Expect.isTrue(result is Two); + Expect.isFalse(result is Two); + + // Uses the type parameter order of Map, not any other type in the hierarchy. + object = new FlippedMap(); + result = extractMapTypeArguments(object, () => new Two()); + // Order is reversed here: + Expect.isTrue(result is Two); + Expect.isFalse(result is Two); +} + +class Two {} + +// 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 { + 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 expand(Function f) => throw new UnimplementedError(); + String get first => throw new UnimplementedError(); + String firstWhere(Function test, {Function orElse}) => + throw new UnimplementedError(); + T fold(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 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 map(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 skip(int count) => throw new UnimplementedError(); + Iterable skipWhile(Function test) => throw new UnimplementedError(); + Iterable take(int count) => throw new UnimplementedError(); + Iterable takeWhile(Function test) => throw new UnimplementedError(); + List toList({bool growable: true}) => throw new UnimplementedError(); + Set toSet() => throw new UnimplementedError(); + Iterable where(Function test) => throw new UnimplementedError(); +} + +class CustomMap implements Map { + bool operator [](Object key) => throw new UnimplementedError(); + void operator []=(int key, bool value) => throw new UnimplementedError(); + void addAll(Map 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 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 get values => throw new UnimplementedError(); +} + +// Note: Flips order of type parameters. +class FlippedMap implements Map { + V operator [](Object key) => throw new UnimplementedError(); + void operator []=(K key, V value) => throw new UnimplementedError(); + void addAll(Map 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 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 get values => throw new UnimplementedError(); +} diff --git a/tests/language_2/language_2_dart2js.status b/tests/language_2/language_2_dart2js.status index 39c082c9966..d9dd488c99b 100644 --- a/tests/language_2/language_2_dart2js.status +++ b/tests/language_2/language_2_dart2js.status @@ -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 diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status index 846056020f9..f996cb15835 100644 --- a/tests/language_2/language_2_dartdevc.status +++ b/tests/language_2/language_2_dartdevc.status @@ -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 diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status index 853032202b8..45dd1e7bae7 100644 --- a/tests/language_2/language_2_kernel.status +++ b/tests/language_2/language_2_kernel.status @@ -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 diff --git a/tests/language_2/language_2_vm.status b/tests/language_2/language_2_vm.status index ebb3d2d9e16..2af057ba93d 100644 --- a/tests/language_2/language_2_vm.status +++ b/tests/language_2/language_2_vm.status @@ -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