Skip to content

Commit

Permalink
Fix runtime cast failures. (#57)
Browse files Browse the repository at this point in the history
* Fix runtime cast failures.

In strong mode, a generic type instantiated with dynamic is not a
subtype of all types. You can't pass a List<dynamic> to something
expecting, say, List<int>.

These errors are usually detected statically, and most of those have
been fixed. However, sometimes this becomes a runtime cast, as in:

    main() {
      // Store a List<dynamic> in a variable of type dynamic.
      dynamic d = [];

      // Implicit runtime downcast from dynamic to List<String>.
      List<String> s = d;
    }

In order to ease the migration to strong mode, DDC has been ignoring
these cast failures when they involve certain commonly used types. We
are now in the process of actively fixing those errors.

More context: dart-lang/sdk#27223

* Update SDK constraints.

* Use newer dev version of the SDK on Travis.

* Bump minimum SDK constraint.
  • Loading branch information
munificent authored Mar 1, 2018
1 parent 957846a commit e93fd28
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 15 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Files and directories created by pub
.dart_tool/
.packages
.pub/
packages
Expand Down
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
language: dart
sudo: false
dart:
- stable
- dev
- 1.23.0
- 2.0.0-dev.23.0
addons:
# otherwise a number of tests in test/security/html_sanitizer_test.dart fail
firefox: "latest"
Expand Down
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
## 0.21.0+1-dev
## 0.21.1-dev

* Updated one of observable's tests to comply with dart 2 voidness semantics
* Updated one test to comply with Dart 2 voidness semantics.
* Fix Dart 2 runtime cast failure in `toObservable()`.
* Loosen `ObservableList.from()` to take `Iterable`, not `Iterable<T>`. This
matches `List.from()` and avoids some unnecessary cast failures.

## 0.21.0

Expand Down
3 changes: 1 addition & 2 deletions lib/src/observable_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ class ObservableList<E> extends ListBase<E> with Observable {

/// Creates an observable list with the elements of [other]. The order in
/// the list will be the order provided by the iterator of [other].
factory ObservableList.from(Iterable<E> other) =>
new ObservableList<E>()..addAll(other);
ObservableList.from(Iterable other) : _list = new List<E>.from(other);

/// The stream of summarized list changes, delivered asynchronously.
///
Expand Down
41 changes: 34 additions & 7 deletions lib/src/to_observable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ library observable.src.to_observable;

import 'dart:collection';

import 'package:dart_internal/extract_type_arguments.dart';

import 'observable.dart' show Observable;
import 'observable_list.dart' show ObservableList;
import 'observable_map.dart' show ObservableMap;
Expand All @@ -30,22 +32,47 @@ toObservable(dynamic value, {bool deep: true}) =>

dynamic _toObservableShallow(dynamic value) {
if (value is Observable) return value;
if (value is Map) return new ObservableMap.from(value);
if (value is Iterable) return new ObservableList.from(value);

if (value is Map) {
return extractMapTypeArguments(value, <K, V>() {
var result = new ObservableMap<K, V>.createFromType(value);
value.forEach((k, v) {
result[_toObservableDeep(k)] = _toObservableDeep(v);
});
return result;
});
}

if (value is Iterable) {
return extractIterableTypeArgument(
value, <T>() => new ObservableList<T>.from(value));
}

return value;
}

dynamic _toObservableDeep(dynamic value) {
if (value is Observable) return value;

if (value is Map) {
var result = new ObservableMap.createFromType(value);
value.forEach((k, v) {
result[_toObservableDeep(k)] = _toObservableDeep(v);
return extractMapTypeArguments(value, <K, V>() {
var result = new ObservableMap<K, V>.createFromType(value);
value.forEach((k, v) {
result[_toObservableDeep(k)] = _toObservableDeep(v);
});
return result;
});
return result;
}

if (value is Iterable) {
return new ObservableList.from(value.map(_toObservableDeep));
return extractIterableTypeArgument(value, <T>() {
var result = new ObservableList<T>();
for (var element in value) {
result.add(_toObservableDeep(element));
}
return result;
});
}

return value;
}
5 changes: 3 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
name: observable
version: 0.21.0+1-dev
version: 0.21.1-dev
author: Dart Team <misc@dartlang.org>
description: Support for marking objects as observable
homepage: https://github.com/dart-lang/observable
environment:
sdk: '>=1.23.0 <2.0.0'
sdk: '>=2.0.0-dev.23.0 <2.0.0'
dependencies:
collection: '^1.11.0'
dart_internal: '^0.1.1'
meta: '^1.0.4'
quiver: '>=0.24.0 <0.29.0'
dev_dependencies:
Expand Down

0 comments on commit e93fd28

Please sign in to comment.