Skip to content

Commit 60e474b

Browse files
nateboschcommit-bot@chromium.org
authored andcommitted
Add section on known bugs
For now stage this in the package:js README - that might not be the best place for it long term since the details may change... - Add a note that makes it explicit we expect all interop to use `package:js` instead of `dart:js`. - Add notes about known limitations and differences between dart2js and DDC. Change-Id: Ib4c967ea1435dd85f41f56646140352b125cee4c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107560 Reviewed-by: Nicholas Shahan <nshahan@google.com> Commit-Queue: Nicholas Shahan <nshahan@google.com>
1 parent f6c0b2d commit 60e474b

1 file changed

Lines changed: 110 additions & 0 deletions

File tree

pkg/js/README.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ end-to-end example.
77

88
### Usage
99

10+
All Dart code interacting with JavaScript should use the utilities provided with
11+
`package:js`. Developers should avoid importing `dart:js` directly.
12+
1013
#### Calling methods
1114

1215
```dart
@@ -94,7 +97,114 @@ DDC there will be no errors despite missing `allowInterop` calls, because DDC
9497
uses JS calling semantics by default. When compiling with Dart2JS the
9598
`allowInterop` utility must be used.
9699

100+
#### Making a Dart function callable from JavaScript
101+
102+
To provide a Dart function callable from JavaScript by name use a setter
103+
annotated with `@JS()`.
104+
105+
```dart
106+
@JS()
107+
library callable_function;
108+
109+
import 'package:js/js.dart';
110+
111+
/// Allows assigning a function to be callable from `window.functionName()`
112+
@JS('functionName')
113+
external set _functionName(void Function() f);
114+
115+
/// Allows calling the assigned function from Dart as well.
116+
@JS()
117+
external void functionName();
118+
119+
void _someDartFunction() {
120+
print('Hello from Dart!');
121+
}
122+
123+
void main() {
124+
_functionName = allowInterop(_someDartFunction);
125+
// JavaScript code may now call `functionName()` or `window.functionName()`.
126+
}
127+
```
128+
129+
## Known limitations and bugs
130+
131+
### Differences betwenn Dart2JS and DDC
132+
133+
Dart's production and development JavaScript compilers use different calling
134+
conventions and type representation, and therefore have different challenges in
135+
JavaScript interop. There are currently some know differences in behavior and
136+
bugs in one or both compilers.
137+
138+
#### allowInterop is required in Dart2JS, optional in DDC
139+
140+
DDC uses the same calling conventions as JavaScript and so Dart functions passed
141+
as callbacks can be invoked without modification. In Dart2JS the calling
142+
conventions are different and so `allowInterop` or `allowInteropCaptureThis`
143+
must be used for any callback.
144+
145+
**Workaround:**: Always use `allowInterop` even when not required in DDC.
146+
147+
#### Callbacks allow extra ignored arguments in DDC
148+
149+
In JavaScript a caller may pass any number of "extra" arguments to a function
150+
and they will be ignored. DDC follows this behavior, Dart2JS will have a runtime
151+
error if a function is invoked with more arguments than expected.
152+
153+
**Workaround:** Write functions that take the same number of arguments as will
154+
be passed from JavaScript. If the number is variable use optional positional
155+
arguments.
156+
157+
#### DDC and Dart2JS have different representation for Maps
158+
159+
Passing a `Map<String, String>` as an argument to a JavaScript function will
160+
have different behavior depending on the compiler. Calling something like
161+
`JSON.stringify()` will give different results.
162+
163+
**Workaround:** Only pass object literals instead of Maps as arguments. For json
164+
specifically use `jsonEncode` in Dart rather than a JS alternative.
165+
166+
#### Missing validation for anonymous factory constructors in DDC
167+
168+
When using an `@anonymous` class to create JavaScript object literals Dart2JS
169+
will enforce that only named arguments are used, while DDC will allow positional
170+
arguments but may generate incorrect code.
171+
172+
**Workaround:** Try builds in both development and release mode to get the full
173+
scope of static validation.
174+
175+
### Sharp Edges
176+
177+
Dart and JavaScript have different semantics and common patterns which makes it
178+
easy to make some mistakes, and difficult for the tools to provide safety. These
179+
sharp edges are known pitfalls.
180+
181+
#### Lack of runtime type checking
182+
183+
The return types of methods annotated with `@JS()` are not validated at runtime,
184+
so an incorrect type may "leak" into other Dart code and violate type system
185+
guarantees.
186+
187+
**Workaround:** For any calls into JavaScript code that are not known to be safe
188+
in their return values, validate the results manually with `is` checks.
189+
190+
#### List instances coming from JavaScript will always be `List<dynamic>`
191+
192+
A JavaScript array does not have a reified element type, so an array returned
193+
from a JavaScript function cannot make guarantees about it's elements without
194+
inspecting each one. At runtime a check like `result is List` may succeed, while
195+
`result is List<String>` will always fail.
196+
197+
**Workaround:** Use a `.cast<String>().toList()` call to get a `List` with the
198+
expected reified type at runtime.
199+
200+
#### The `JsObject` type from `dart:js` can't be used with `@JS()` annotation
201+
202+
`JsObject` and related code in `dart:js` uses a different approach and may not
203+
be passed as an argument to a method annotated with `@JS()`.
97204

205+
**Workaround:** Avoid importing `dart:js` and only use the `package:js` provided
206+
approach. To handle object literals use `@anonymous` on an `@JS()` annotated
207+
class.
98208

99209
## Reporting issues
100210

0 commit comments

Comments
 (0)