mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
![Nicholas Shahan](/assets/img/avatar_default.png)
In the new type system, allow native types that have a "peer" on the Dart side to use that Dart type as their reified type. This matches the semantics of the old type system for these types. Change-Id: I62bf6cb1654d33549a6ea2c757e7e65fa81677a4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/313700 Reviewed-by: Srujan Gaddam <srujzs@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Nicholas Shahan <nshahan@google.com>
352 lines
9.9 KiB
Dart
352 lines
9.9 KiB
Dart
// Copyright (c) 2019, 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.
|
|
|
|
// Requirements=nnbd
|
|
|
|
@JS()
|
|
library js_interop_test;
|
|
|
|
import 'dart:_foreign_helper' as helper show JS;
|
|
import 'dart:_interceptors' as interceptors;
|
|
import 'dart:_runtime' as dart;
|
|
import 'dart:js' show context;
|
|
|
|
import 'package:expect/expect.dart';
|
|
import 'package:js/js.dart';
|
|
|
|
@JS()
|
|
class Console {
|
|
@JS()
|
|
external void log(arg);
|
|
}
|
|
|
|
@JS('console')
|
|
external Console get console;
|
|
|
|
@JS('console.log')
|
|
external Function get _log;
|
|
|
|
@JS('console.log')
|
|
external void log(String s);
|
|
|
|
String dartStaticMethod() => 'hello';
|
|
|
|
@JS('jsStaticVariable')
|
|
external Function? get _jsStaticVariable;
|
|
|
|
@JS('jsStaticVariable')
|
|
external set _jsStaticVariable(Function? f);
|
|
|
|
@JS('jsStaticVariable')
|
|
external void jsStaticVariable(String s);
|
|
|
|
@JS('jsStaticFunction')
|
|
external Function get _jsStaticFunction;
|
|
|
|
@JS('jsStaticFunction')
|
|
external set _jsStaticFunction(Function f);
|
|
|
|
@JS('jsStaticFunction')
|
|
external void jsStaticFunction(String Function() f);
|
|
|
|
@JS()
|
|
class SomeClass {
|
|
external factory SomeClass(String Function() f);
|
|
external set jsFunctionFieldSetter(String Function() f);
|
|
external void Function(String Function() f) get jsFunctionFieldGetter;
|
|
external String jsInstanceMethod(String Function() f);
|
|
|
|
external NestedJs get jsNonFunctionField;
|
|
}
|
|
|
|
@JS()
|
|
@anonymous
|
|
class NestedJs {
|
|
external factory NestedJs({required String Function() constructorArg});
|
|
external String get stringField;
|
|
}
|
|
|
|
@JS('someClass')
|
|
external set _someClass(dynamic s);
|
|
@JS('someClass')
|
|
external SomeClass get someClass;
|
|
|
|
void main() {
|
|
Function(String) jsFunc = helper.JS('', '(x) => {}');
|
|
Expect.equals(dart.assertInterop(jsFunc), jsFunc);
|
|
|
|
Expect.equals(dart.assertInterop(_log), _log);
|
|
Expect.equals(dart.assertInterop(console.log), console.log);
|
|
Expect.throws(() => dart.assertInterop(dartStaticMethod));
|
|
|
|
Expect.isNull(_jsStaticVariable);
|
|
_jsStaticVariable = jsFunc;
|
|
Expect.isNotNull(_jsStaticVariable);
|
|
Expect.equals(dart.assertInterop(_jsStaticVariable), _jsStaticVariable);
|
|
|
|
final dynamic wrappedDartStaticMethod = allowInterop(dartStaticMethod);
|
|
|
|
final Function localNonNullLegacy = () => 'hello';
|
|
final String Function() localNonNull = () => 'hello';
|
|
final Function? localNullableLegacy = () => 'hello';
|
|
final String Function()? localNullable = () => 'hello';
|
|
|
|
// Assignment to JS static field.
|
|
Expect.throws(() {
|
|
_jsStaticVariable = () => 'hello';
|
|
});
|
|
Expect.throws(() {
|
|
_jsStaticVariable = dartStaticMethod;
|
|
});
|
|
Expect.throws(() {
|
|
_jsStaticVariable = localNonNullLegacy;
|
|
});
|
|
Expect.throws(() {
|
|
_jsStaticVariable = localNonNull;
|
|
});
|
|
Expect.throws(() {
|
|
_jsStaticVariable = localNullableLegacy!;
|
|
});
|
|
Expect.throws(() {
|
|
_jsStaticVariable = localNullable!;
|
|
});
|
|
_jsStaticVariable = allowInterop(dartStaticMethod);
|
|
_jsStaticVariable = wrappedDartStaticMethod;
|
|
|
|
// Argument to static JS function.
|
|
Function(Function(String), String) jsFunc2 = helper.JS('', '(f) => f()');
|
|
_jsStaticFunction = jsFunc2;
|
|
Expect.throws(() {
|
|
jsStaticFunction(() => 'hello');
|
|
});
|
|
Expect.throws(() {
|
|
jsStaticFunction(dartStaticMethod);
|
|
});
|
|
Expect.throws(() {
|
|
jsStaticFunction(localNonNullLegacy as String Function());
|
|
});
|
|
Expect.throws(() {
|
|
jsStaticFunction(localNonNull);
|
|
});
|
|
Expect.throws(() {
|
|
jsStaticFunction(localNullableLegacy as String Function());
|
|
});
|
|
Expect.throws(() {
|
|
jsStaticFunction(localNullable!);
|
|
});
|
|
jsStaticFunction(allowInterop(() => 'hello'));
|
|
jsStaticFunction(wrappedDartStaticMethod);
|
|
|
|
// Argument to torn off static JS function
|
|
dynamic method = jsStaticFunction;
|
|
Expect.throws(() {
|
|
method(() => 'hello');
|
|
});
|
|
Expect.throws(() {
|
|
method(dartStaticMethod);
|
|
});
|
|
Expect.throws(() {
|
|
method(localNonNullLegacy);
|
|
});
|
|
Expect.throws(() {
|
|
method(localNonNull);
|
|
});
|
|
Expect.throws(() {
|
|
method(localNullableLegacy!);
|
|
});
|
|
Expect.throws(() {
|
|
method(localNullable!);
|
|
});
|
|
method(allowInterop(() => 'hello'));
|
|
method(wrappedDartStaticMethod);
|
|
|
|
// Assignment to instance field.
|
|
_someClass = helper.JS(
|
|
'',
|
|
'{"jsInstanceMethod": function(f) {return f();}, '
|
|
'"jsNonFunctionField": {"stringField":"hello js"}, '
|
|
'"jsFunctionFieldGetter": function(f) {return f();}}');
|
|
Expect.throws((() {
|
|
someClass.jsFunctionFieldSetter = () => 'hello';
|
|
}));
|
|
Expect.throws((() {
|
|
someClass.jsFunctionFieldSetter = dartStaticMethod;
|
|
}));
|
|
Expect.throws((() {
|
|
someClass.jsFunctionFieldSetter = localNonNullLegacy as String Function();
|
|
}));
|
|
Expect.throws((() {
|
|
someClass.jsFunctionFieldSetter = localNonNull;
|
|
}));
|
|
Expect.throws((() {
|
|
someClass.jsFunctionFieldSetter = localNullableLegacy as String Function();
|
|
}));
|
|
Expect.throws((() {
|
|
someClass.jsFunctionFieldSetter = localNullable!;
|
|
}));
|
|
someClass.jsFunctionFieldSetter = allowInterop(() => 'hello');
|
|
someClass.jsFunctionFieldSetter = wrappedDartStaticMethod;
|
|
|
|
// Argument to instance method.
|
|
Expect.throws(() {
|
|
someClass.jsInstanceMethod(() => 'hello');
|
|
});
|
|
Expect.throws(() {
|
|
someClass.jsInstanceMethod(dartStaticMethod);
|
|
});
|
|
Expect.throws(() {
|
|
someClass.jsInstanceMethod(localNonNullLegacy as String Function());
|
|
});
|
|
Expect.throws(() {
|
|
someClass.jsInstanceMethod(localNonNull);
|
|
});
|
|
Expect.throws(() {
|
|
someClass.jsInstanceMethod(localNullableLegacy as String Function());
|
|
});
|
|
Expect.throws(() {
|
|
someClass.jsInstanceMethod(localNullable!);
|
|
});
|
|
someClass.jsInstanceMethod(allowInterop(() => 'hello'));
|
|
someClass.jsInstanceMethod(wrappedDartStaticMethod);
|
|
|
|
// Argument to constructor.
|
|
context.callMethod('eval', ['function SomeClass(a) { a(); }']);
|
|
Expect.throws(() {
|
|
SomeClass(() => 'hello');
|
|
});
|
|
Expect.throws(() {
|
|
SomeClass(dartStaticMethod);
|
|
});
|
|
Expect.throws(() {
|
|
SomeClass(localNonNullLegacy as String Function());
|
|
});
|
|
Expect.throws(() {
|
|
SomeClass(localNonNull);
|
|
});
|
|
Expect.throws(() {
|
|
SomeClass(localNullableLegacy as String Function());
|
|
});
|
|
Expect.throws(() {
|
|
SomeClass(localNullable!);
|
|
});
|
|
SomeClass(allowInterop(() => 'hello'));
|
|
SomeClass(wrappedDartStaticMethod);
|
|
|
|
// Argument to anonymous constructor.
|
|
Expect.throws(() {
|
|
NestedJs(constructorArg: () => 'hello');
|
|
});
|
|
Expect.throws(() {
|
|
NestedJs(constructorArg: dartStaticMethod);
|
|
});
|
|
Expect.throws(() {
|
|
NestedJs(constructorArg: localNonNullLegacy as String Function());
|
|
});
|
|
Expect.throws(() {
|
|
NestedJs(constructorArg: localNonNull);
|
|
});
|
|
Expect.throws(() {
|
|
NestedJs(constructorArg: localNullableLegacy as String Function());
|
|
});
|
|
Expect.throws(() {
|
|
NestedJs(constructorArg: localNullable!);
|
|
});
|
|
NestedJs(constructorArg: allowInterop(() => 'hello'));
|
|
NestedJs(constructorArg: wrappedDartStaticMethod);
|
|
|
|
// Argument to torn off instance method.
|
|
method = someClass.jsInstanceMethod;
|
|
Expect.throws(() {
|
|
method(() => 'hello');
|
|
});
|
|
Expect.throws(() {
|
|
method(dartStaticMethod);
|
|
});
|
|
Expect.throws(() {
|
|
method(localNonNullLegacy);
|
|
});
|
|
Expect.throws(() {
|
|
method(localNonNull);
|
|
});
|
|
Expect.throws(() {
|
|
method(localNullableLegacy!);
|
|
});
|
|
Expect.throws(() {
|
|
method(localNullable!);
|
|
});
|
|
method(allowInterop(() => 'hello'));
|
|
method(wrappedDartStaticMethod);
|
|
|
|
// Function typed getter
|
|
Expect.throws(() {
|
|
someClass.jsFunctionFieldGetter(() => 'hello');
|
|
});
|
|
Expect.throws(() {
|
|
someClass.jsFunctionFieldGetter(dartStaticMethod);
|
|
});
|
|
Expect.throws(() {
|
|
someClass.jsFunctionFieldGetter(localNonNullLegacy as String Function());
|
|
});
|
|
Expect.throws(() {
|
|
someClass.jsFunctionFieldGetter(localNonNull);
|
|
});
|
|
Expect.throws(() {
|
|
someClass.jsFunctionFieldGetter(localNullableLegacy as String Function());
|
|
});
|
|
Expect.throws(() {
|
|
someClass.jsFunctionFieldGetter(localNullable!);
|
|
});
|
|
someClass.jsFunctionFieldGetter(allowInterop(() => 'hello'));
|
|
someClass.jsFunctionFieldGetter(wrappedDartStaticMethod);
|
|
|
|
// Stored Function typed getter
|
|
method = someClass.jsFunctionFieldGetter;
|
|
// We lose safety after calling a getter that returns a function, which takes
|
|
// a function as an argument. Since this can be modeled with a method, instead
|
|
// of a getter returning a function, we don't expect this is a pattern likely
|
|
// to show up in real code.
|
|
//Expect.throws(() {
|
|
// method(() => 'hello');
|
|
//});
|
|
//Expect.throws(() {
|
|
// method(dartStaticMethod);
|
|
//});
|
|
//Expect.throws(() {
|
|
// method(localNonNullLegacy);
|
|
//});
|
|
//Expect.throws(() {
|
|
// method(localNonNull);
|
|
//});
|
|
//Expect.throws(() {
|
|
// method(localNullableLegacy as String Function());
|
|
//});
|
|
//Expect.throws(() {
|
|
// method(localNullable!);
|
|
//});
|
|
method(allowInterop(() => 'hello'));
|
|
method(wrappedDartStaticMethod);
|
|
|
|
// Non-function fields
|
|
Expect.equals('hello js', someClass.jsNonFunctionField.stringField,
|
|
'Does not wrap access to a field');
|
|
|
|
// No such method errors from interop calls.
|
|
// The current behavior is that DDC does not treat these errors from the
|
|
// JavaScript side as a LegacyJavaScriptObject.
|
|
Expect.throwsNoSuchMethodError(
|
|
() => context.callMethod('eval', ['self.foo()']));
|
|
Expect.throws<interceptors.JSNoSuchMethodError>(
|
|
() => context.callMethod('eval', ['self.foo()']));
|
|
var error = Expect.throws(() => context.callMethod('eval', ['self.foo()']));
|
|
Expect.notType<interceptors.LegacyJavaScriptObject>(error);
|
|
Expect.notEquals(interceptors.LegacyJavaScriptObject, error.runtimeType);
|
|
Expect.throwsNoSuchMethodError(
|
|
() => context.callMethod('eval', ['self.foo.bar()']));
|
|
Expect.throws<interceptors.JSNoSuchMethodError>(
|
|
() => context.callMethod('eval', ['self.foo.bar()']));
|
|
error = Expect.throws(() => context.callMethod('eval', ['self.foo.bar()']));
|
|
Expect.notType<interceptors.LegacyJavaScriptObject>(error);
|
|
Expect.notEquals(interceptors.LegacyJavaScriptObject, error.runtimeType);
|
|
}
|