ed009afc4c
Bug: https://github.com/dart-lang/sdk/issues/37896 dart2js disallows JS interop classes from extending Dart classes, and ddc does not work as expected. Dart classes that extends JS interop classes can't be declared in ddc and throw an error on usage in dart2js. This CL adds static errors for both cases. Change-Id: I72001d2e8bec046c0e1ab4c06fed8fcf84ade259 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164840 Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Srujan Gaddam <srujzs@google.com>
445 lines
7.6 KiB
Dart
445 lines
7.6 KiB
Dart
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
// @dart = 2.7
|
|
|
|
library jsinterop.abstract_test;
|
|
|
|
import 'package:expect/expect.dart';
|
|
import 'package:async_helper/async_helper.dart';
|
|
import 'package:compiler/src/common.dart';
|
|
import 'package:compiler/src/commandline_options.dart';
|
|
import '../helpers/memory_compiler.dart';
|
|
|
|
const List<Test> TESTS = const <Test>[
|
|
const Test('Empty js-interop class.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
class A {}
|
|
|
|
main() => new A();
|
|
''', warnings: const []),
|
|
const Test('Js-interop class with external method.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
class A {
|
|
external method();
|
|
}
|
|
|
|
main() => new A();
|
|
'''),
|
|
const Test(
|
|
'Js-interop class with external method with required parameters.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
class A {
|
|
external method(a, b);
|
|
}
|
|
|
|
main() => new A();
|
|
'''),
|
|
const Test(
|
|
'Js-interop class with external method with optional parameters.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
class A {
|
|
external method([a, b]);
|
|
}
|
|
|
|
main() => new A();
|
|
'''),
|
|
const Test(
|
|
'Js-interop class with external method with optional parameters '
|
|
'with default values.',
|
|
'''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
class A {
|
|
external method([a = 1, b = 2]);
|
|
}
|
|
|
|
main() => new A();
|
|
'''),
|
|
const Test('Js-interop class with static method.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
class A {
|
|
static method() {}
|
|
}
|
|
|
|
main() => new A();
|
|
'''),
|
|
const Test(
|
|
'Js-interop class with abstract getter.',
|
|
'''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
class A {
|
|
get foo;
|
|
}
|
|
|
|
main() => new A();
|
|
''',
|
|
warnings: const [MessageKind.ABSTRACT_GETTER],
|
|
skipForKernel: true),
|
|
const Test('Js-interop class that extends a js-interop class.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
abstract class A {
|
|
method();
|
|
}
|
|
|
|
@JS()
|
|
class B extends A {
|
|
external method();
|
|
}
|
|
|
|
main() => new B();
|
|
'''),
|
|
const Test(
|
|
'Js-interop class that extends a js-interop class, '
|
|
'reversed declaration order.',
|
|
'''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
class B extends A {
|
|
external method();
|
|
}
|
|
|
|
@JS()
|
|
abstract class A {
|
|
method();
|
|
}
|
|
|
|
main() => new B();
|
|
'''),
|
|
const Test.multi(
|
|
'Js-interop class that extends a js-interop class from a different '
|
|
'library.',
|
|
const {
|
|
'main.dart': '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
import 'other.dart';
|
|
|
|
@JS()
|
|
class B extends A {
|
|
external method();
|
|
}
|
|
|
|
main() => new B();
|
|
''',
|
|
'other.dart': '''
|
|
@JS()
|
|
library other;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
abstract class A {
|
|
method();
|
|
}
|
|
'''
|
|
}),
|
|
const Test('Js-interop class that implements a regular class.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
abstract class A {
|
|
method();
|
|
}
|
|
|
|
@JS()
|
|
class B implements A {
|
|
external method();
|
|
}
|
|
|
|
main() => new B();
|
|
'''),
|
|
const Test('Js-interop class that implements a js-interop class.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
abstract class A {
|
|
method();
|
|
}
|
|
|
|
@JS()
|
|
class B implements A {
|
|
external method();
|
|
}
|
|
|
|
main() => new B();
|
|
'''),
|
|
const Test('Js-interop class with generative constructor.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
class A {
|
|
external A();
|
|
}
|
|
|
|
main() => new A();
|
|
'''),
|
|
const Test('Js-interop class with factory constructor.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
class A {
|
|
factory A() => null;
|
|
}
|
|
|
|
main() => new A();
|
|
'''),
|
|
const Test('Empty anonymous js-interop class.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
@anonymous
|
|
class A {}
|
|
|
|
main() => new A();
|
|
'''),
|
|
const Test('Anonymous js-interop class with generative constructor.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
@anonymous
|
|
class A {
|
|
external A();
|
|
}
|
|
|
|
main() => new A();
|
|
'''),
|
|
const Test('Anonymous js-interop class with factory constructor.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
@anonymous
|
|
class A {
|
|
factory A() => null;
|
|
}
|
|
|
|
main() => new A();
|
|
'''),
|
|
const Test(
|
|
'Anonymous js-interop class with external factory constructor.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
@anonymous
|
|
class A {
|
|
external factory A();
|
|
}
|
|
|
|
main() => new A();
|
|
'''),
|
|
const Test('External factory constructor with named parameters.', '''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
@anonymous
|
|
class A {
|
|
external factory A({a, b});
|
|
}
|
|
|
|
main() => new A(a: 1);
|
|
'''),
|
|
const Test(
|
|
'External factory constructor with named parameters '
|
|
'with default parameters.',
|
|
'''
|
|
@JS()
|
|
library test;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
@anonymous
|
|
class A {
|
|
external factory A({a: 1, b: 2});
|
|
}
|
|
|
|
main() => new A(a: 1);
|
|
'''),
|
|
const Test('Function-typed return type', '''
|
|
@JS()
|
|
library lib;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS('func')
|
|
external int Function() func();
|
|
|
|
main() {
|
|
func();
|
|
}
|
|
'''),
|
|
const Test(
|
|
'Non-external field.',
|
|
'''
|
|
@JS()
|
|
library lib;
|
|
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
@anonymous
|
|
class B {
|
|
int Function() callback;
|
|
}
|
|
|
|
@JS('makeB')
|
|
external B makeB();
|
|
|
|
main() {
|
|
makeB().callback();
|
|
}
|
|
''',
|
|
// TODO(34174): Disallow js-interop fields.
|
|
/*errors: const [MessageKind.IMPLICIT_JS_INTEROP_FIELD_NOT_SUPPORTED]*/
|
|
),
|
|
];
|
|
|
|
void main(List<String> args) {
|
|
asyncTest(() async {
|
|
for (Test test in TESTS) {
|
|
bool run = true;
|
|
if (args.isNotEmpty) {
|
|
run = false;
|
|
for (String arg in args) {
|
|
if (test.name.contains(arg)) {
|
|
run = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (run) {
|
|
await runTest(test);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
class Test {
|
|
final String name;
|
|
final String _source;
|
|
final Map<String, String> _sources;
|
|
final List<MessageKind> errors;
|
|
final List<MessageKind> warnings;
|
|
final bool skipForKernel;
|
|
|
|
const Test(this.name, this._source,
|
|
{this.errors: const <MessageKind>[],
|
|
this.warnings: const <MessageKind>[],
|
|
this.skipForKernel: false})
|
|
: _sources = null;
|
|
|
|
const Test.multi(this.name, this._sources,
|
|
{this.errors: const <MessageKind>[],
|
|
this.warnings: const <MessageKind>[],
|
|
this.skipForKernel: false})
|
|
: _source = null;
|
|
|
|
String get source => _source != null ? _source : _sources['main.dart'];
|
|
|
|
Map<String, String> get sources =>
|
|
_source != null ? {'main.dart': _source} : _sources;
|
|
}
|
|
|
|
runTest(Test test) async {
|
|
print('==${test.name}======================================================');
|
|
print(test.source);
|
|
if (!test.skipForKernel) {
|
|
await runTestInternal(test);
|
|
}
|
|
}
|
|
|
|
runTestInternal(Test test) async {
|
|
DiagnosticCollector collector = new DiagnosticCollector();
|
|
List<String> options = <String>[];
|
|
// TODO(redemption): Enable inlining.
|
|
options.add(Flags.disableInlining);
|
|
await runCompiler(
|
|
diagnosticHandler: collector,
|
|
options: options,
|
|
memorySourceFiles: test.sources);
|
|
Expect.equals(
|
|
test.errors.length, collector.errors.length, 'Unexpected error count.');
|
|
Expect.equals(test.warnings.length, collector.warnings.length,
|
|
'Unexpected warning count.');
|
|
for (int index = 0; index < test.errors.length; index++) {
|
|
Expect.equals(test.errors[index],
|
|
collector.errors.elementAt(index).messageKind, 'Unexpected error.');
|
|
}
|
|
for (int index = 0; index < test.warnings.length; index++) {
|
|
Expect.equals(test.warnings[index],
|
|
collector.warnings.elementAt(index).messageKind, 'Unexpected warning.');
|
|
}
|
|
}
|