ba40d9bece
Change-Id: I73edeeaf4934899b71d6335dcb4b4b54dce46bae Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/426340 Auto-Submit: Bob Nystrom <rnystrom@google.com> Reviewed-by: Nate Biggs <natebiggs@google.com> Commit-Queue: Nate Biggs <natebiggs@google.com>
386 lines
10 KiB
Dart
386 lines
10 KiB
Dart
// Copyright (c) 2022, 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.
|
|
|
|
// TODO(srujzs): Break this test up into multiple tests, delete redundant tests,
|
|
// and move them to a shared library.
|
|
|
|
import 'dart:js_util';
|
|
import 'package:expect/expect.dart';
|
|
import 'package:js/js.dart';
|
|
import 'static_interop_library.dart';
|
|
|
|
@JS()
|
|
external void eval(String code);
|
|
|
|
@JS('JSClass')
|
|
@staticInterop
|
|
class StaticJSClass {
|
|
external factory StaticJSClass.factory(String foo);
|
|
|
|
external static String externalStaticMethod();
|
|
static String staticMethod() => 'bar';
|
|
}
|
|
|
|
extension StaticJSClassMethods on StaticJSClass {
|
|
external String foo;
|
|
external String sum(String a, String? b, String c);
|
|
external set nonNullableInt(int d);
|
|
external int get nonNullableInt;
|
|
external set nullableInt(int? d);
|
|
external int? get nullableInt;
|
|
external int nonNullableIntReturnMethod();
|
|
external int? nullableIntReturnMethod(bool returnNull);
|
|
external String doSum1Or2(String a, [String? b]);
|
|
external String doSumUpTo2([String? a, String? b]);
|
|
external String doSum1Or2NonNull(String a, [String b = 'bar']);
|
|
external String doSumUpTo2NonNull([String a = 'foo', String b = 'bar']);
|
|
external int doIntSum1Or2(int a, [int? b]);
|
|
external int doIntSumUpTo2([int? a, int? b]);
|
|
external int doIntSum1Or2NonNull(int a, [int b = 2]);
|
|
external int doIntSumUpTo2NonNull([int a = 1, int b = 2]);
|
|
|
|
@JS('nameInJSMethod')
|
|
external String nameInDartMethod(String a, String b);
|
|
@JS('nameInJSGetter')
|
|
external String get nameInDartGetter;
|
|
@JS('nameInJSSetter')
|
|
external set nameInDartSetter(String v);
|
|
external String get nameInJSSetter;
|
|
}
|
|
|
|
void createClassTest() {
|
|
eval(r'''
|
|
globalThis.JSClass = function(foo) {
|
|
this.foo = foo;
|
|
this.nonNullableInt = 6;
|
|
this.nameInJSGetter = 'foo';
|
|
this.nonNullableIntReturnMethod = function() {
|
|
return 7;
|
|
}
|
|
this.nullableIntReturnMethod = function(returnNull) {
|
|
if (returnNull) {
|
|
return null;
|
|
} else {
|
|
return 8;
|
|
}
|
|
}
|
|
this.sum = function(a, b, c) {
|
|
if (b == null) b = ' ';
|
|
return a + b + c;
|
|
}
|
|
this.doSum1Or2 = function(a, b) {
|
|
return a + (b ?? 'bar');
|
|
}
|
|
this.doSumUpTo2 = function(a, b) {
|
|
return (a ?? 'foo') + (b ?? 'bar');
|
|
}
|
|
this.doSum1Or2NonNull = function(a, b) {
|
|
return (a ?? 'foo') + (b ?? 'bar');
|
|
}
|
|
this.doSumUpTo2NonNull = function(a, b) {
|
|
return (a ?? 'foo') + (b ?? 'bar');
|
|
}
|
|
this.doIntSum1Or2 = function(a, b) {
|
|
return a + (b ?? 2);
|
|
}
|
|
this.doIntSumUpTo2 = function(a, b) {
|
|
return (a ?? 1) + (b ?? 2);
|
|
}
|
|
this.doIntSum1Or2NonNull = function(a, b) {
|
|
return a + (b ?? 2);
|
|
}
|
|
this.doIntSumUpTo2NonNull = function(a, b) {
|
|
return (a ?? 1) + (b ?? 2);
|
|
}
|
|
this.nameInJSMethod = function(a, b) {
|
|
return a + b;
|
|
}
|
|
}
|
|
globalThis.JSClass.externalStaticMethod = function() {
|
|
return 'foo';
|
|
}
|
|
|
|
globalThis.library = function() {}
|
|
globalThis.library.libraryTopLevelGetter = 'foo';
|
|
globalThis.library.jsedLibraryTopLevelGetter = 'foo';
|
|
globalThis.library.NamespacedClass = function() {
|
|
this.member = function() {
|
|
return 'foo';
|
|
}
|
|
}
|
|
''');
|
|
Expect.equals('foo', StaticJSClass.externalStaticMethod());
|
|
Expect.equals('bar', StaticJSClass.staticMethod());
|
|
|
|
final foo = StaticJSClass.factory('foo');
|
|
Expect.equals('foo', foo.foo);
|
|
foo.foo = 'bar';
|
|
Expect.equals('bar', foo.foo);
|
|
Expect.equals('hello world!!', foo.sum('hello', null, 'world!!'));
|
|
Expect.equals(null, foo.nullableInt);
|
|
foo.nullableInt = 5;
|
|
Expect.equals(5, foo.nullableInt);
|
|
Expect.equals(6, foo.nonNullableInt);
|
|
foo.nonNullableInt = 16;
|
|
Expect.equals(16, foo.nonNullableInt);
|
|
Expect.equals(7, foo.nonNullableIntReturnMethod());
|
|
Expect.equals(8, foo.nullableIntReturnMethod(false));
|
|
Expect.equals(null, foo.nullableIntReturnMethod(true));
|
|
|
|
Expect.equals('foobar', foo.doSum1Or2('foo'));
|
|
Expect.equals('foobar', foo.doSum1Or2('foo', 'bar'));
|
|
Expect.equals('foobar', foo.doSumUpTo2());
|
|
Expect.equals('foobar', foo.doSumUpTo2('foo'));
|
|
Expect.equals('foobar', foo.doSumUpTo2('foo', 'bar'));
|
|
|
|
Expect.equals('foobar', foo.doSum1Or2NonNull('foo'));
|
|
Expect.equals('foobar', foo.doSum1Or2NonNull('foo', 'bar'));
|
|
Expect.equals('foobar', foo.doSumUpTo2NonNull());
|
|
Expect.equals('foobar', foo.doSumUpTo2NonNull('foo'));
|
|
Expect.equals('foobar', foo.doSumUpTo2NonNull('foo', 'bar'));
|
|
|
|
Expect.equals(3, foo.doIntSum1Or2(1));
|
|
Expect.equals(3, foo.doIntSum1Or2(1, 2));
|
|
Expect.equals(3, foo.doIntSumUpTo2());
|
|
Expect.equals(3, foo.doIntSumUpTo2(1));
|
|
Expect.equals(3, foo.doIntSumUpTo2(1, 2));
|
|
|
|
Expect.equals(3, foo.doIntSum1Or2NonNull(1));
|
|
Expect.equals(3, foo.doIntSum1Or2NonNull(1, 2));
|
|
Expect.equals(3, foo.doIntSumUpTo2NonNull());
|
|
Expect.equals(3, foo.doIntSumUpTo2NonNull(1));
|
|
Expect.equals(3, foo.doIntSumUpTo2NonNull(1, 2));
|
|
|
|
Expect.equals('foobar', foo.nameInDartMethod('foo', 'bar'));
|
|
Expect.equals('foo', foo.nameInDartGetter);
|
|
foo.nameInDartSetter = 'boo';
|
|
Expect.equals('boo', foo.nameInJSSetter);
|
|
|
|
Expect.equals('foo', NamespacedClass().member());
|
|
Expect.equals('foo', libraryTopLevelGetter);
|
|
Expect.equals('foo', libraryOtherTopLevelGetter);
|
|
}
|
|
|
|
@JS('JSClass.NestedJSClass')
|
|
@staticInterop
|
|
class NestedJSClass {
|
|
external factory NestedJSClass.factory(String foo);
|
|
}
|
|
|
|
extension NestedJSClassMethods on NestedJSClass {
|
|
external String foo;
|
|
}
|
|
|
|
void createClassWithNestedJSNameTest() {
|
|
eval(r'''
|
|
globalThis.JSClass = {};
|
|
globalThis.JSClass.NestedJSClass = function(foo) {
|
|
this.foo = foo;
|
|
};
|
|
''');
|
|
final foo = NestedJSClass.factory('foo');
|
|
Expect.equals(foo.foo, 'foo');
|
|
}
|
|
|
|
@JS('JSParent')
|
|
@staticInterop
|
|
class StaticJSParent {
|
|
external factory StaticJSParent.factory();
|
|
}
|
|
|
|
extension StaticJSParentMethods on StaticJSParent {
|
|
external set child(StaticJSChild child);
|
|
external String childsFoo();
|
|
}
|
|
|
|
@JS('JSChild')
|
|
@staticInterop
|
|
class StaticJSChild {
|
|
external factory StaticJSChild.factory();
|
|
}
|
|
|
|
extension StaticJSChildMethods on StaticJSChild {
|
|
external set foo(String s);
|
|
}
|
|
|
|
void setInteropPropertyTest() {
|
|
eval(r'''
|
|
globalThis.JSParent = function() {
|
|
this.child = null;
|
|
this.childsFoo = () => {
|
|
return this.child.foo;
|
|
}
|
|
}
|
|
|
|
globalThis.JSChild = function() {
|
|
this.foo = null;
|
|
}
|
|
''');
|
|
|
|
final parent = StaticJSParent.factory();
|
|
final child = StaticJSChild.factory();
|
|
parent.child = child;
|
|
child.foo = 'boo';
|
|
Expect.equals('boo', parent.childsFoo());
|
|
}
|
|
|
|
class DartObject {
|
|
final int x;
|
|
|
|
DartObject(this.x);
|
|
}
|
|
|
|
@JS('JSHolder')
|
|
@staticInterop
|
|
class StaticJSHolder {
|
|
external factory StaticJSHolder.factory(DartObject object);
|
|
}
|
|
|
|
extension StaticJSHolderMethods on StaticJSHolder {
|
|
external DartObject foo;
|
|
}
|
|
|
|
void setDartObjectPropertyTest() {
|
|
eval(r'''
|
|
globalThis.JSHolder = function(foo) {
|
|
this.foo = foo;
|
|
}
|
|
''');
|
|
final traveler = DartObject(4);
|
|
final holder = StaticJSHolder.factory(traveler);
|
|
Expect.equals(traveler, holder.foo);
|
|
}
|
|
|
|
@JS()
|
|
external String get foo;
|
|
|
|
@JS()
|
|
external String? get blu;
|
|
|
|
@JS('')
|
|
external void set baz(String);
|
|
|
|
@JS('boo.bar')
|
|
external String get bam;
|
|
|
|
@JS('bar')
|
|
external String fooBar(String);
|
|
|
|
void topLevelMethodsTest() {
|
|
eval(r'''
|
|
globalThis.foo = 'bar';
|
|
globalThis.baz = null;
|
|
globalThis.boo = {
|
|
'bar': 'jam'
|
|
}
|
|
globalThis.bar = function(string) {
|
|
return string + ' ' + globalThis.baz;
|
|
}
|
|
''');
|
|
|
|
Expect.equals(foo, 'bar');
|
|
Expect.equals(blu, null);
|
|
Expect.equals(bam, 'jam');
|
|
baz = 'world!';
|
|
Expect.equals(fooBar('hello'), 'hello world!');
|
|
}
|
|
|
|
@JS()
|
|
@anonymous
|
|
@staticInterop
|
|
class AnonymousJSClass {
|
|
external factory AnonymousJSClass.factory({
|
|
String? foo,
|
|
String bar = 'baz',
|
|
String? bleep,
|
|
int? goo,
|
|
int ooo = 1,
|
|
List<double>? saz,
|
|
List<double> zoo = const [1.0, 2.0],
|
|
});
|
|
}
|
|
|
|
extension AnonymousJSClassExtension on AnonymousJSClass {
|
|
external String? get foo;
|
|
external String? get bar;
|
|
external String? get bleep;
|
|
external int? get goo;
|
|
external int? get ooo;
|
|
external List<Object?>? saz;
|
|
external List<double>? zoo;
|
|
}
|
|
|
|
void anonymousTest() {
|
|
final anonymousJSClass = AnonymousJSClass.factory(
|
|
foo: 'boo',
|
|
bleep: 'bleep',
|
|
saz: const [1.0, 2.0],
|
|
goo: 0,
|
|
);
|
|
Expect.equals('boo', anonymousJSClass.foo);
|
|
Expect.equals(null, anonymousJSClass.bar);
|
|
Expect.equals('bleep', anonymousJSClass.bleep);
|
|
Expect.equals(0, anonymousJSClass.goo);
|
|
Expect.equals(null, anonymousJSClass.ooo);
|
|
Expect.listEquals(const [1.0, 2.0], anonymousJSClass.saz!);
|
|
Expect.equals(null, anonymousJSClass.zoo);
|
|
}
|
|
|
|
@JS()
|
|
@anonymous
|
|
@staticInterop
|
|
class AnonymousRedirectJSClass {
|
|
external factory AnonymousRedirectJSClass._({String Function(String)? foo});
|
|
|
|
factory AnonymousRedirectJSClass.concrete(String Function(String) foo) =>
|
|
AnonymousRedirectJSClass._(foo: allowInterop(foo));
|
|
}
|
|
|
|
extension AnonymousRedirectJSClassExtension on AnonymousRedirectJSClass {
|
|
external String foo(String bar);
|
|
}
|
|
|
|
void concreteFactoryConstructorTest() {
|
|
final anonymousRedirectJSClass = AnonymousRedirectJSClass.concrete(
|
|
(String bar) => bar + bar,
|
|
);
|
|
Expect.equals('foofoo', anonymousRedirectJSClass.foo('foo'));
|
|
}
|
|
|
|
@JS()
|
|
@staticInterop
|
|
class JSArray {}
|
|
|
|
extension JSArrayExtension on JSArray {
|
|
external Object? operator [](int index);
|
|
external int get length;
|
|
external String get deoptKey;
|
|
}
|
|
|
|
@JS()
|
|
external JSArray get arrayObject;
|
|
|
|
void staticInteropBypassConversionTest() {
|
|
eval(r'''
|
|
globalThis.arrayObject = ['1', '2'];
|
|
globalThis.arrayObject.deoptKey = 'foo';
|
|
''');
|
|
|
|
JSArray a = arrayObject;
|
|
Expect.isTrue(instanceOfString(a, "Array"));
|
|
Expect.equals(2, a.length);
|
|
Expect.equals('foo', a.deoptKey);
|
|
}
|
|
|
|
void main() {
|
|
createClassTest();
|
|
createClassWithNestedJSNameTest();
|
|
setInteropPropertyTest();
|
|
setDartObjectPropertyTest();
|
|
topLevelMethodsTest();
|
|
anonymousTest();
|
|
concreteFactoryConstructorTest();
|
|
staticInteropBypassConversionTest();
|
|
}
|