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

Exposing a Dart function to JS behaves differently in compiled vs non-compiled mode #17752

Closed
tfortes opened this issue Mar 24, 2014 · 8 comments
Labels
type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)

Comments

@tfortes
Copy link

tfortes commented Mar 24, 2014

What steps will reproduce the problem?

  1. context['foo'] = myDartFn;
  2. context['foo'] is JsFunction;

What is the expected output? What do you see instead?
In non-compiled mode this returns true.
In compiled mode (to js) this returns false.

I'd expect it to behave the same way regardless.

What version of the product are you using? On what operating system?
1_3_0_dev_6_0

@jacob314
Copy link
Member

This is a bug in the Dartium implementation of how we proxy functions.


cc @justinfagnani.
Set owner to @jacob314.
Added Area-Dartium, Triaged labels.

@DartBot
Copy link

DartBot commented May 29, 2014

This comment was originally written by chirayuk@gmail.com


I just ran across this today.  The workaround (which wasn't obvious to me) is to write,

    context['foo'] = new JsFunction.withThis((_, arg1, arg2, arg3, …) => myDartFn(arg1, arg2, arg2, …));

that works in both compiled and non-compiled modes.  Since there is no JsFunction constructor without "withThis", one has to repeat the argument list manually for each function that's exposed (i.e. you can't get away with a helper to do the wrapping.)

@jacob314
Copy link
Member

Set owner to @justinfagnani.

@DartBot
Copy link

DartBot commented May 29, 2014

This comment was originally written by chirayu@google.com

@DartBot
Copy link

DartBot commented Jun 4, 2014

This comment was originally written by chirayu@chirayuk.com


I've used a somewhat generic work around for AngularDart → chirayuk/angular.dart@6797274

 This isn't a hot codepath (used for E2E testing and debugging) so an actual fix isn't a priority.

For those curious, here's the gist of the workaround:

    // Work around http://dartbug.com/17752
    //
    // To proxy a dart function to JS, use _jsFunction(dartFunction).
    // To proxy a dart map/iterable/list to JS, use _jsify(obj).

    // Helper function to JSify a Dart object. While this is required to JSify
    // the result of a scope.eval(), other uses are not required and are used to
    // work around http://dartbug.com/17752 in a convenient way (that bug affects
    // dart2js in checked mode.)
    _jsify(var obj) {
      if (obj == null || obj is JsObject) {
        return obj;
      }
      if (obj is Function) {
        return _jsFunction(obj);
      }
      if ((obj is Map) || (obj is Iterable)) {
        var mappedObj = (obj is Map) ?
            new Map.fromIterables(obj.keys, obj.values.map(_jsify)) : obj.map(_jsify);
        if (obj is List) {
          return new JsArray.from(mappedObj);
        } else {
          return new JsObject.jsify(mappedObj);
        }
      }
      return obj;
    }

    // Proxies a Dart function that accepts up to 10 parameters.
    JsFunction _jsFunction(Function fn) {
      const Object X = __varargSentinel;
      Function fnCopy = fn; // workaround a bug.
      return new JsFunction.withThis(
          (thisArg, [o1=X, o2=X, o3=X, o4=X, o5=X, o6=X, o7=X, o8=X, o9=X, o10=X]) {
            // Work around a bug in dart 1.4.0 where the closurized variable, fn,
            // gets mysteriously replaced with our own closure function leading to a
            // stack overflow.
            fn = fnCopy;
            if (o10 == null && identical(o9, X)) {
              // Work around another bug in dart 1.4.0. This bug is not present in
              // dart 1.5.0-dev.2.0.
              // In dart 1.4.0, when running in Dartium (not dart2js), if you invoke
              // a JsFunction from Dart code (either by calling .apply([args]) on it
              // or by calling .callMethod(jsFuncName, [args]) on a JsObject
              // containing the JsFunction, regardless of whether you specified the
              // thisArg keyword parameter, the Dart function is called with the
              // first argument in the thisArg param causing all the arguments to be
              // shifted by one. We can detect this by the fact that o10 is null
              // but o9 is X (should only happen when o9 got a default value) and
              // work around it by using thisArg as the first parameter.
              return __invokeFromJs(fn, thisArg, o1, o2, o3, o4, o5, o6, o7, o8, o9);
            } else {
              return __invokeFromJs(fn, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10);
            }
          }
          );
    }

    __invokeFromJs(fn, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10) {
      var args = [o1, o2, o3, o4, o5, o6, o7, o8, o9, o10];
      while (args.length > 0 && identical(args.last, __varargSentinel)) {
        args.removeLast();
      }
      return _jsify(Function.apply(fn, args));
    }

    const Object __varargSentinel = const Object();

@kevmoo kevmoo added type-bug Incorrect behavior (everything from a crash to more subtle misbehavior) and removed priority-unassigned labels Feb 29, 2016
@ahirschberg
Copy link

I'm getting a similar issue, but I don't think any of the solutions posted here will fix it. I'm using a javascript library with my dart code, and in dartium the interop works fine. When I use the debugger, I can see that my JSObject is set correctly:
.

However after compiling via dart2js, the exact same code fails because of additional wrapping of the JSObject:
.

Is there a workaround for this?

@jkriesel
Copy link

jkriesel commented Sep 6, 2016

I run into the exact same issue when trying to interface with ag_grid. Is there a solution? If you have control of the resulting javascript I've found that ._jsObject DOES behave the way you'd like. In my case I don't really want to modify the core library to make it work with dart, but I might be forced to do so.

@ahirschberg
Copy link

I ended up writing a quick and dirty JS wrapper that intercepts the objects before they reach the javascript library and de-dartifying the object. That's pretty ugly though, it would be nice to see a proper fix for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
None yet
Development

No branches or pull requests

8 participants