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

unexpected dart2js error with external element

Closed
a14n opened this issue Jan 12, 2017 · 16 comments
Closed

unexpected dart2js error with external element #28371

a14n opened this issue Jan 12, 2017 · 16 comments

Comments

@a14n
Copy link
Contributor

a14n commented Jan 12, 2017

I face an issue with angular-2.2.0 and sdk-1.20.1 by running dart2js web/index.dart with :

import 'package:angular2/platform/browser.dart';
abstract class _LatLngBounds {
  external factory _LatLngBounds();
}
class MainApp {}
main() {
  bootstrap(MainApp);
}

The error is :

web/index.dart:4:3:
Error: External method without an implementation.
  external factory _LatLngBounds();
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Hint: 4 hint(s) suppressed in package:angular2.
Error: Compilation failed.

This error doesn't appear without angular2 if the index.dart content is only :

abstract class _LatLngBounds {
  external factory _LatLngBounds();
}
main() async {
  print(_LatLngBounds);
}
@sigmundch
Copy link
Member

sigmundch commented Jan 12, 2017

@a14n - I wonder if there is a missing @JS annotation here to declare _LatLngBOunds as an interop type or something like that?

The difference with or without angular could be related to whether or not _LatLngBounds is consider to be instantiated by dart2js. In the simple example dart2js can tell that you only see the type, but that you never call it's constructor, so we don't complain.

Is angular2 in this mode also using mirrors or some other feature that will lead dart2js to think that this class is intantiated? Do you see the same error if you try to call new _LatLngBounds in your main method?

@a14n
Copy link
Contributor Author

a14n commented Jan 13, 2017

The problem here is similar to a more complex case with my google_maps package.

dart-google-maps uses those kinds of abstract classes as template to generate classes (eg. _LatLng is used to generate LatLng). Those classes are actually never used (like in the above example) and not instantiated.

However I have a more complex project with the same version of google_maps and angular2 that does not lead to this error. So it is really strange and hard to understand.

@a14n
Copy link
Contributor Author

a14n commented Jan 13, 2017

BTW I face the same issue with sdk-1.21.0

@sigmundch
Copy link
Member

sigmundch commented Jan 13, 2017

mmm.. not sure what's going on yet. My guess is that something is making dart2js believe that this constructor could be instantiated - is dart:mirrors used in this configuration? Does the problem go a way when you run the ng2 transformers to get rid of dart:mirrors?

@a14n
Copy link
Contributor Author

a14n commented Jan 13, 2017

With the single web/index.dart file :

import 'package:angular2/core.dart';
import 'package:angular2/platform/browser.dart';
abstract class _LatLngBounds {
  external factory _LatLngBounds();
}
@Component(
    selector: 'main-app',
    template: 'foo'
)
class MainApp {}
main() => bootstrap(MainApp);

and pubspec.yaml :

name: angular_maps
dependencies:
  angular2: ^2.2.0
transformers:
- angular2:
    platform_directives:
    - 'package:angular2/common.dart#COMMON_DIRECTIVES'
    platform_pipes:
    - 'package:angular2/common.dart#COMMON_PIPES'
    entry_points: web/index.dart

then pub build succeed.

If I remove the transformer section to have a pubspec.yaml like :

name: angular_maps
dependencies:
  angular2: ^2.2.0

then pub build fails with the following error :

External method without an implementation.
  external factory _LatLngBounds();
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Now it becomes even funnier!

If I split web/index.dart in 2 files :

// web/index.dart
import 'package:angular_maps/file.dart' as app;
main() => app.main();

// lib/part.dart
import 'package:angular2/core.dart';
import 'package:angular2/platform/browser.dart';
abstract class _LatLngBounds {
  external factory _LatLngBounds();
}
@Component(
    selector: 'main-app',
    template: 'foo'
)
class MainApp {}
main() => bootstrap(MainApp);

then pub build fails with or without the transformer section in pubspec.yaml.

@a14n
Copy link
Contributor Author

a14n commented Jan 13, 2017

I forgot to mention that in the cases where pub build failed I see the dart:mirrors warning in the output.

@sigmundch
Copy link
Member

sigmundch commented Jan 13, 2017

That makes sense - the angular2 transformer removes the use of dart:mirrors (typically needed for di purposes).

Practically speaking, your code above is equivalent to something like this:

import 'dart:mirrors';

abstract class _A {
  external factory _A();
}

class _B {}

main() {
  reflectClass(_B).newInstance(const Symbol(''), []);
}

When dart2js sees the use of reflection, it becomes very conservative and assumes anything can be reflected and instantiated. This example is a bit silly because you can clearly tell that _A is never created, but you can imagine how quickly this is no longer tractable.

It seems to me that the fix for this needs to be either on the angular side or in the dart-google-maps package (or the generator in js_wrapping). In particular, the use of external methods is very limited and we only allow it when we can tell where the implementation comes from: either from a patch in the core libraries or from js-interop (the class is annotated with @JS or something equivalent). Tree-shaking was letting you ignore this limitation.

It seems to me that you are using this external method just to seed a code generator, but you never expect to call new _LatLngBounds() (only the generated new LatLngBoudns()). Is that correct?

I can think of 2 options to fix the issue:

  • adapt angular2 @MirrorsUsed annotations (if they have any) to ensure that these factory constructors are excluded and help dart2js realize that these are never invoked.
  • remove external and make the factory contain a empty body. It will look a bit uglier, but it's valid Dart at that point. This would require changing js_wrapping to recognize a slightly different pattern.

My personal preference would be to fix js_wrapping: that will allow you to have valid code regardless of how it is used.

I'll close this bug shortly since I believe there is no action item on the dart2js side of things, but feel free to reopen if I misunderstood the problem.

Cheers!

@a14n
Copy link
Contributor Author

a14n commented Jan 13, 2017

Thanks a lot for your help. I will try to find a workaround with your explanations in mind.

@a14n
Copy link
Contributor Author

a14n commented Jan 13, 2017

First try, bad try! I crashed the compiler :D

I resurrected an old pattern I used some times ago (factory _LatLngBounds() = dynamic;) :

import 'package:angular2/core.dart';
import 'package:angular2/platform/browser.dart';

abstract class _LatLngBounds {
  factory _LatLngBounds() = dynamic;
  get a;
}
@Component(
    selector: 'main-app',
    template: 'foo'
)
class MainApp {}

main() => bootstrap(MainApp);

with the error :

Build error:
Transform Dart2JS on angular_maps|web/index3.dart threw error: unsupported operation on erroneous element
package:compiler_unsupported/src/elements/modelx.dart 236      ErroneousElementX.unsupported
package:compiler_unsupported/src/elements/modelx.dart 247      ErroneousElementX.functionSignature
package:compiler_unsupported/src/ssa/builder.dart 5058         SsaBuilder.visitRedirectingFactoryBody
package:compiler_unsupported/src/tree/nodes.dart 1570          RedirectingFactoryBody.accept
package:compiler_unsupported/src/ssa/builder.dart 736          SsaBuilder.buildMethod
package:compiler_unsupported/src/ssa/builder.dart 282          SsaBuilder.build
package:compiler_unsupported/src/ssa/builder.dart 80           SsaBuilderTask.build.<fn>.<fn>
package:compiler_unsupported/src/compiler.dart 1623            CompilerDiagnosticReporter.withCurrentElement
package:compiler_unsupported/src/ssa/builder.dart 72           SsaBuilderTask.build.<fn>
package:compiler_unsupported/src/common/tasks.dart 63          CompilerTask.measure
package:compiler_unsupported/src/ssa/builder.dart 70           SsaBuilderTask.build
package:compiler_unsupported/src/ssa/ssa.dart 40               SsaFunctionCompiler.compile
package:compiler_unsupported/src/js_backend/backend.dart 1548  JavaScriptBackend.codegen
package:compiler_unsupported/src/common/codegen.dart 243       CodegenWorkItem.run
package:compiler_unsupported/src/compiler.dart 866             Compiler.emptyQueue.<fn>.<fn>.<fn>.<fn>.<fn>
package:compiler_unsupported/src/common/tasks.dart 176         CompilerTask.measureSubtask
package:compiler_unsupported/src/compiler.dart 866             Compiler.emptyQueue.<fn>.<fn>.<fn>.<fn>
package:compiler_unsupported/src/common/tasks.dart 176         CompilerTask.measureSubtask
package:compiler_unsupported/src/compiler.dart 864             Compiler.emptyQueue.<fn>.<fn>.<fn>
package:compiler_unsupported/src/compiler.dart 1623            CompilerDiagnosticReporter.withCurrentElement
package:compiler_unsupported/src/compiler.dart 862             Compiler.emptyQueue.<fn>.<fn>
package:compiler_unsupported/src/enqueue.dart 749              EnqueuerStrategy.processWorkItem
package:compiler_unsupported/src/js_backend/enqueuer.dart 452  CodegenEnqueuer.forEach
package:compiler_unsupported/src/compiler.dart 858             Compiler.emptyQueue.<fn>
package:compiler_unsupported/src/common/tasks.dart 176         CompilerTask.measureSubtask
package:compiler_unsupported/src/compiler.dart 857             Compiler.emptyQueue
package:compiler_unsupported/src/compiler.dart 886             Compiler.processQueue.<fn>
package:compiler_unsupported/src/common/tasks.dart 176         CompilerTask.measureSubtask
package:compiler_unsupported/src/compiler.dart 875             Compiler.processQueue
package:compiler_unsupported/src/compiler.dart 757             Compiler.compileLoadedLibraries.<fn>
package:compiler_unsupported/src/common/tasks.dart 176         CompilerTask.measureSubtask
package:compiler_unsupported/src/compiler.dart 646             Compiler.compileLoadedLibraries
package:compiler_unsupported/src/compiler.dart 554             Compiler.runInternal.<fn>

Build failed.

@sigmundch
Copy link
Member

sigmundch commented Jan 13, 2017

oh my - I'll open a separate bug for that.

factory _LatLngBounds() => null should hopefully work.

@sigmundch
Copy link
Member

sigmundch commented Jan 13, 2017

factory _LtLngBounds() = Object should work too.

@a14n
Copy link
Contributor Author

a14n commented Jan 13, 2017

factory _LatLngBounds() = Object works fine. I'll try to go with this pattern.

@sigmundch
Copy link
Member

sigmundch commented Jan 13, 2017

Filed at #28392

Note that dynamic is a type annotation but not an actual class with a constructor, that's why we crash in that case. It's equivalent to factory _LatLngBounds() = unresolved_name;

@a14n
Copy link
Contributor Author

a14n commented Jan 13, 2017

Good to know. Thanks for the advice!

@a14n
Copy link
Contributor Author

a14n commented Jan 13, 2017

Oops, pattern factory _LatLngBounds() = Object is not strong-mode compatible.

ERROR: The redirected constructor '() → Object' has incompatible parameters with '() → _LatLngBounds'.

Let's go with factory _LatLngBounds() => null;.

@frankpepermans
Copy link

frankpepermans commented May 24, 2017

Just a FYI, I was having the exact same issue as described here, but I was able to solve it as following:

My dart library which declared the interop classes was importing import 'package:js/js.dart'; as js

My annotations would accordingly look like @js.JS()

When I then ran build, I got the same error messages, after some efforts I've simply removed the alias and instead just used import 'package:js/js.dart';

Now the errors are gone (this is with SDK 1.23.0 and Angular 2.0)

a14n added a commit to a14n/dart-js-wrapping that referenced this issue Jun 20, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants