Open annagrin opened 10 months ago
CC @srujzs
Btw looks like there is a naming problem (call$1
vs call
)- here is prototype generated by dart2js for the closure in question:
A._findNonce_closure.prototype = {
call$1(element) {
var nonceValue = A._asString(type$.JavaScriptObject._as(element).nonce),
t1 = $.$get$_noncePattern();
if (t1._nativeRegExp.test(nonceValue))
this._box_0.nonce = nonceValue;
},
$signature: 77
};
I haven't debugged the code yet, but I'm debating two theories:
dart:html
and package:web
(since NodeList
is an intercepted type tooLooking at the output of client.js, I believe (b) is more likely than (a). First, I see the code that calls .forEach
, and it appears to be the raw JSInterop invocation from the extension method and not an intercepted call from dart:html
(in that case I'd expect the call to be .forEach$1
). The fact that the NoSuchMethod error also shows 3 arguments above also leads me to believe this could be (b).
Would it work if you change the code above to not just expect Node
, but Node, int, NodeList
(matching what MDN describes here https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach)? The second and third argument could be optional too: (Node element, [int index, NodeList list]) { ... }.toJS
.
I haven't tried the original code in dwds, but I can confirm that a simple example with mismatched arguments shows a similar error.
Here is an isolated repro:
import 'dart:js_interop';
import 'dart:js_interop_unsafe';
extension type F(JSObject o) implements JSObject {
external void forEach(JSFunction f);
}
@JS()
external F get f;
main() {
globalContext.callMethod('eval'.toJS,
'window.f = {forEach(g) { g(1,2); },};'.toJS);
f.forEach(((JSAny a, JSAny b) => print('$a $b')).toJS); // good
f.forEach(((JSAny a) => print('$a')).toJS); // oh no!
}
This produces this error when running in d8:
1 2
/usr/local/google/home/sigmund/dart/git_all/sdk/sdk/lib/_internal/js_runtime/lib/preambles/d8.js:263: NoSuchMethodError: method not found: 'call'
Receiver: Closure 'main_closure0'
Arguments: [1, 2]
throw e;
^
NoSuchMethodError: method not found: 'call'
Receiver: Closure 'main_closure0'
Arguments: [1, 2]
at Object.wrapException (/tmp/out.js:669:43)
at main_closure0.noSuchMethod$1 (/tmp/out.js:3863:15)
at Object.noSuchMethod$1$ (/tmp/out.js:398:42)
at Object.Primitives_functionNoSuchMethod (/tmp/out.js:554:16)
at Object.Primitives__generalApplyFunction (/tmp/out.js:607:18)
at Object.Primitives_applyFunction (/tmp/out.js:586:16)
at _callDartFunctionFast (/tmp/out.js:2978:16)
at /tmp/out.js:2968:18
at Object.forEach (eval at JSObjectUnsafeUtilExtension__callMethod (/tmp/out.js:2958:29), <anonymous>:1:26)
at Object.callMethod (/tmp/out.js:2987:31)
Finished with a exit code: 1
It has often been requested that we are more permissive with extra arguments on callbacks, so it may be worth revisiting that in the near future
the workaround for me was to not use the "foreach" and just iterate with an index:) Leaving this as a bug though because it would be great if this example did not compile.
I think Siggi nailed it that there's a mismatch in the number of parameters of the callback and the number of arguments JS provides. Here's the request for wrapped Dart functions to accept more parameters: https://github.com/dart-lang/sdk/issues/48186
the workaround for me was to not use the "foreach" and just iterate with an index:) Leaving this as a bug though because it would be great if this example did not compile.
I think this is hard to do given the current way we write JS types. We don't know how many args a given callback is expected to take with JSFunction
. There is a general feature request I've been meaning to file and thinking about which is making JSFunction
a generic type that accepts a function type.
In this case, it would look something like JSFunction<void Function(Node, int, NodeList)>
, so passing any arbitrary JSFunction
won't work. This would make the above code not compile (but of course, if you casted, it'll compile).
This is a little example that might be useful.
class CallableObject {
const CallableObject();
void call() {
print("CallableObject called");
}
}
class ValueContainer<T> {
ValueContainer(this.value);
final T value;
final value2 = const CallableObject();
}
void main() {
final b = ValueContainer(const CallableObject());
b.value2(); // Runs correctly and calls the "call" function
b.value.call(); // Also runs correctly
b.value(); // NoSuchMethodError: tried to call a non-function, such as null: 'b.value'
}
I think that's a tangential issue as it doesn't use callbacks or interop. I've filed a separate issue here: https://github.com/dart-lang/sdk/issues/54461
The following code causes a runtime error, not sure what the proper fix would be:
Code
Runtime error
Repro
dart run build_runner build
in webdev/dwds directorywebdev/fixtures/_webdevSoundSmoke
example in VSCode