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

List.cast() is not a native Array at browser env. with js-interop (same error between DDC and Dart2JS) #35622

Open
jumperchen opened this Issue Jan 10, 2019 · 3 comments

Comments

Projects
None yet
2 participants
@jumperchen
Copy link

jumperchen commented Jan 10, 2019

Dart version: 2.1.0

We use a their-party library to interop with JS and they declare a List<String> as setValue() parameter, so in Dart 2, we need to pass a List<dynamic> to List<String> by using list.cast().

For example,

// this is foo.dart
@JS()
library foo;

import 'package:js/js.dart';

@JS('foo')
class Foo {
  external factory Foo();
  external void setValue(List<String> data); // this library declares List<String> here
}
// this is index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script defer src="main.dart.js" ></script>
    <script>
        window.foo = function() {
          this.setValue = function (v) {
              console.log(v); // print "CastList<dynamic, String>" here
              for (var i = 0; i < v.length; i++) {
                  console.log(v[i]);  // <- print "undefined" here
              }
          }
        };
    </script>
</head>
<body>

</body>
</html>

As you can see above, in the JS implementation, it will print "undefined" there.

// this is main.dart
import 'foo.dart';

main() {
  var list = [];
  list.add("test");
  Foo().setValue(list.cast()); // we need to cast here, otherwise, Dart will throw runtime error or compile time error.
}

If the list = []; without .cast(), it will be a native Array at browser env, so is the inconsistent behavior expected?

@jumperchen

This comment has been minimized.

Copy link

jumperchen commented Jan 11, 2019

We are trying to avoid this issue from our side, but it seems only to copy whole List to a new List and passes the new one to a js-interop method to solve the JS array issue.
Because we cannot guarantee the list which passed from outside of the method whether has been casted or not.
Any suggestion?

@sigmundch

This comment has been minimized.

Copy link
Member

sigmundch commented Jan 11, 2019

@jumperchen thanks for reaching out.

There is a lot of work happening these days to improve js-interop, @jmesserly can hopefully provide more details.

As you noted, some operations in Dart that help provide appropriate strong types don't play well with JS-interop.

In this case, there are a few options you can use:

  • create the list with the right type initially:
var list = <String>[]; // now you have a List<String> that is represented as a JsArray internally
  • create a copy of the list, instead of using .cast:
setValue(new List<String>.from(list));
  • use a weaker type in your JS-interop interface
void setValue(List<Object> ...);

@jmesserly - do we have plans to support non-native List types (like CastList) or plans to provide hints to guide developers on this?

@jumperchen

This comment has been minimized.

Copy link

jumperchen commented Jan 18, 2019

@sigmundch Thanks for the suggestion, we will use new List<String>.from() for the moment, and wait for @jmesserly's response.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment