Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cast error when awaiting enumerateDevices() #39627

Closed
davidjenkins opened this issue Dec 3, 2019 · 11 comments
Closed

Cast error when awaiting enumerateDevices() #39627

davidjenkins opened this issue Dec 3, 2019 · 11 comments
Assignees
Labels
area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. web-libraries Issues impacting dart:html, etc., libraries

Comments

@davidjenkins
Copy link

Dart version 2.7.0 (build 2.7.0-dev.2.1 e4344a5) -- included with Flutter (Win 10 x64)

This issue also occurs in current dev branch of Flutter, which uses Dart 2.7.0 (not sure if "2.7.0" alone means "2.7.0-dev0.0").

If I try this:

import 'dart:html';
// ...
await window.navigator.mediaDevices.enumerateDevices();

Then I get this (in Chrome):

Type 'List<dynamic>' should be 'List<MediaDeviceInfo>' to implement expected type 'FutureOr<List<MediaDeviceInfo>>'.

Full stack:

errors.dart:147 Uncaught (in promise) Error: Type 'List<dynamic>' should be 'List<MediaDeviceInfo>' to implement expected type 'FutureOr<List<MediaDeviceInfo>>'.
    at Object.throw_ [as throw] (errors.dart:196)
    at Object.castError (errors.dart:45)
    at Object.cast [as as] (operations.dart:426)
    at Function.check_FutureOr [as _check] (future.dart:44)
    at js_util_dart2js.dart:145
@srawlins srawlins added the area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. label Dec 4, 2019
@sigmundch sigmundch added the web-libraries Issues impacting dart:html, etc., libraries label Dec 4, 2019
@sigmundch
Copy link
Member

@terrylucas - this appears like an error in the generated dart:html code.

I believe we'll hit this issue in other APIs as well, basically anywhere we have promiseToFuture<List<X>> today, I expect the underlying list to be a List<dynamic> (or List<JSAny> once we add support for it)

dart-bot pushed a commit that referenced this issue Dec 13, 2019
Bug: #39627

Enumerating media devices gives back a cast error in html_dart2js. This
test should fail and the cast should be fixed such that it passes.

Change-Id: Ie2502f265448d078578113cbea57dd81d20015c7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/128104
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
dart-bot pushed a commit that referenced this issue Dec 13, 2019
Bug: #39627

Lists in a future should be typed with dynamic since they come from JS
interop.

Results from running tools/dom/scripts/go.sh

Change-Id: I4ed37d6f5fa570beef71652dcad17c3bcf7560ab
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/128300
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Srujan Gaddam <srujzs@google.com>
dart-bot pushed a commit that referenced this issue Dec 14, 2019
Bug: #39627

Lists in a future should be typed with dynamic since they come from JS
interop.

Change-Id: I93d4da16eb27c3af23820a170cfad7cc5c3b4472
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/128368
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Srujan Gaddam <srujzs@google.com>
@rakudrama
Copy link
Member

rakudrama commented Dec 17, 2019

Can we make JavaScript Arrays support JSArray<any> instead of these changes that make the API harder to use?

@sigmundch
Copy link
Member

is any exposed in any way that we can declare it today (for both dart2js and ddc)?

dart-bot pushed a commit that referenced this issue Dec 17, 2019
Bug: #39627

Changelog reflects changes in
https://dart-review.googlesource.com/c/sdk/+/128368.

Change-Id: Idace6e7c7af81ebd9e0b61639e239205e55e53d8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/128640
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
@srujzs
Copy link
Contributor

srujzs commented Dec 19, 2019

I'm going to mark this as closed since the original issue has been addressed. Please feel free to reopen or open a separate issue if this can be cleaned up.

@srujzs srujzs closed this as completed Dec 19, 2019
@mnordine
Copy link
Contributor

mnordine commented Jan 4, 2020

I see this is closed and it was noted that the original issue has been addressed. Does that mean that the code above now returns the expected types?

e.g. Can I do:

import 'dart:html';

main()
{
  final devices = await window.navigator.mediaDevices.enumerateDevices();
  print(devices.first.runtimeType); // MediaDeviceInfo
  print(devices.first.deviceId);
}

@sigmundch
Copy link
Member

@mnordine - I haven't verified this, but yes, I expect that the individual elements will contain the expected type.

final devices = await window.navigator.mediaDevices.enumerateDevices();
print(decices.runtimeType); // List<dynamic>
print(devices.first.runtimeType); // MediaDeviceInfo

because the list type is dynamic and not List<Object>, unfortuantely this means that you wont get a static error if you accidentally do:

devices.first.nonExistingProperty

You can however use the static type to avoid this problem:

MediaDeviceInfo device = devices.first;
print(device.deviceId);

Note that the fix landed in b84d6ba, which will be included in the next dev channel release of the sdk.

@mnordine
Copy link
Contributor

mnordine commented Jan 6, 2020

Sorry if I'm misunderstanding, but was the solution here to make all web apis that return lists return them as List<dynamic>, therefore losing the typing?

If so, is this temporary?

Also, I don't understand how, if devices.runtimeType is List<dynamic>, a member of that list could be anything other than dynamic?

@sigmundch
Copy link
Member

Sorry if I'm misunderstanding, but was the solution here to make all web apis that return lists return them as List<dynamic>

Correct, this was necessary because of how types are represented in the JavaScript program. When you create a list by hand in Dart, our compiler generates code that will store the type-argument together with that list. However, when the list is created outside the Dart program (e.g. from DOM APIs in the browser, or other JavaScript libraries), the list is a plain JavaScript arrays which is not tagged in any way.

An alternative approach is to wrap the lists to provide a different static type, but we have learned in the past that wrapping on every API call is expensive. Modifying the underlying JavaScript array to include our type-parameter tag is sometimes not safe (since it can affect the external JavaScript code in the case of JS-interop), but we should look more into whether this is possible in some cases like tehse.

For now, we adjusted the type to represent more closely what the browser is giving us: an untagged list, which today we interpret as a List<dynamic>

... therefore losing the typing?

This is unfortunate, yes. In the future, we are planning to have a different static type for this List<JsAny>, which helps us distinguish that this list doesn't contain instances of any type in Dart (dynamic), but possibly any type that could come from JavaScript/the browser.

Also, I don't understand how, if devices.runtimeType is List<dynamic>, a member of that list could be anything other than dynamic?

When we say List<dynamic> we are saying that the list is allowed to contain elements of any type. Similarly List<Object> would allow any instance that extends from Object, which again would include any type.

It so happens that we know from the browser that all instances in that list will be instances of MediaDeviceInfo. So if you were to wrap the list and cast it, e.g. using List.cast you could get a safer static type:

final devices = (await window.navigator.mediaDevices.enumerateDevices()).cast<MediaDeviceInfo>();
print(decices.runtimeType); // List<MediaDeviceInfo>
print(devices.first.runtimeType); // MediaDeviceInfo

(* this is the kind of wrapping operation that we could have added automatically, but we didn't to avoid the performance cost... it's not out of the question to reevaluate the tradeoffs and reconsider this)

@mnordine
Copy link
Contributor

mnordine commented Jan 6, 2020

Appreciate the explanation and the cast tip.

we know from the browser that all instances in that list will be instances of MediaDeviceInfo

Given the above, why can't it be List<MediaDeviceInfo> then?

I think I understand you're saying you don't want users to pay the tax for adding the cast code for every call on those types. What I don't understand is how you're getting the types of the List elements themselves "from the browser"

@sigmundch
Copy link
Member

What I don't understand is how you're getting the types of the List elements themselves "from the browser"

We use JavaScript's object.constructor.name to find the type associated with the object instance. In this case the constructor name for a single element in the list will be MediaDeviceInfo.

The list is different because the browser creates a list whose constructor name is simply Array, so just by looking at the instance we have no way of knowing which kind of lists it is. To associate the type as List<MediaDeviceInfo> we would need to either wrap/cast inside the API (as with the tip above) or add a way to attach the type information on the API boundary (this is not something that is supported today).

Hope this helps clarify things a bit.

@mnordine
Copy link
Contributor

mnordine commented Jan 6, 2020

Makes sense, thx again

slimlime added a commit to QRolo/QRolo that referenced this issue Feb 22, 2021
…her than `as` dynamic assert

See dart-lang/sdk#39627 . Fix `as` assertion causes a runtime exception if value
types do no match. cast<T> generic type reify made more concrete creates a new iterable List
slimlime added a commit to QRolo/QRolo that referenced this issue Feb 22, 2021
…her than `as` dynamic assert

See dart-lang/sdk#39627 . Fix `as` assertion causes a runtime exception if value
types do no match. cast<T> generic type reify made more concrete creates a new iterable List
copybara-service bot pushed a commit that referenced this issue Nov 9, 2021
Change-Id: I51d6f6759520fb5703065f9b223a65e47bed56f4
Bug: #39627
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/219560
Auto-Submit: Riley Porter <rileyporter@google.com>
Commit-Queue: Riley Porter <rileyporter@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
copybara-service bot pushed a commit that referenced this issue Nov 10, 2021
Change-Id: I51d6f6759520fb5703065f9b223a65e47bed56f4
Bug: #39627
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/219560
Auto-Submit: Riley Porter <rileyporter@google.com>
Commit-Queue: Riley Porter <rileyporter@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. web-libraries Issues impacting dart:html, etc., libraries
Projects
None yet
Development

No branches or pull requests

6 participants