Files
sdk/tests/lib/js/extends_test/extends_test_util.dart
T
Srujan Gaddam 4cd0926703 [package:js] Add/modify tests for JS interop subtyping
Adds some basic tests to check for runtime subtyping with JS
interop types. Uses function types only to check for subtyping
relationships. Extends some tests on is and as checks as well.

Change-Id: I9cbc91d0c475fba3213dd9a3443921232c286804
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/170660
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
2020-11-11 19:28:13 +00:00

211 lines
5.2 KiB
Dart

// Copyright (c) 2020, 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.
// Tests inheritance relationships between `JS` and `anonymous` classes/objects.
@JS()
library extends_test;
import 'package:expect/minitest.dart';
import 'package:js/js.dart';
@JS()
external void eval(String code);
@JS()
class JSClass {
external int get a;
external int getA();
external int getAOrB();
}
@JS()
@anonymous
class AnonymousClass {
external int get a;
external int getA();
}
@JS()
class JSExtendJSClass extends JSClass {
external JSExtendJSClass(int a, int b);
external int get b;
external int getB();
external int getAOrB();
}
@JS()
class JSExtendAnonymousClass extends AnonymousClass {
external JSExtendAnonymousClass(int a, int b);
external int get b;
external int getB();
}
@JS()
@anonymous
class AnonymousExtendAnonymousClass extends AnonymousClass {
external int get b;
external int getB();
}
@JS()
@anonymous
class AnonymousExtendJSClass extends JSClass {
external int get b;
external int getB();
external int getAOrB();
}
external AnonymousExtendAnonymousClass get anonExtendAnon;
external AnonymousExtendJSClass get anonExtendJS;
void useJSClass(JSClass js) {}
void useAnonymousClass(AnonymousClass a) {}
void setUpWithoutES6Syntax() {
// Use the old way to define inheritance between JS objects.
eval(r"""
function inherits(child, parent) {
if (child.prototype.__proto__) {
child.prototype.__proto__ = parent.prototype;
} else {
function tmp() {};
tmp.prototype = parent.prototype;
child.prototype = new tmp();
child.prototype.constructor = child;
}
}
function JSClass(a) {
this.a = a;
this.getA = function() {
return this.a;
}
this.getAOrB = function() {
return this.getA();
}
}
function JSExtendJSClass(a, b) {
JSClass.call(this, a);
this.b = b;
this.getB = function() {
return this.b;
}
this.getAOrB = function() {
return this.getB();
}
}
inherits(JSExtendJSClass, JSClass);
function JSExtendAnonymousClass(a, b) {
this.a = a;
this.b = b;
this.getA = function() {
return this.a;
}
this.getB = function() {
return this.b;
}
this.getAOrB = function() {
return this.getB();
}
}
self.anonExtendAnon = new JSExtendAnonymousClass(1, 2);
self.anonExtendJS = new JSExtendJSClass(1, 2);
""");
}
void setUpWithES6Syntax() {
// Use the ES6 syntax for classes to make inheritance easier.
eval(r"""
class JSClass {
constructor(a) {
this.a = a;
}
getA() {
return this.a;
}
getAOrB() {
return this.getA();
}
}
class JSExtendJSClass extends JSClass {
constructor(a, b) {
super(a);
this.b = b;
}
getB() {
return this.b;
}
getAOrB() {
return this.getB();
}
}
self.JSExtendJSClass = JSExtendJSClass;
class JSExtendAnonymousClass {
constructor(a, b) {
this.a = a;
this.b = b;
}
getA() {
return this.a;
}
getB() {
return this.b;
}
getAOrB() {
return this.getB();
}
}
self.JSExtendAnonymousClass = JSExtendAnonymousClass;
self.anonExtendAnon = new JSExtendAnonymousClass(1, 2);
self.anonExtendJS = new JSExtendJSClass(1, 2);
""");
}
void testInheritance() {
// Note that for the following, there are no meaningful tests for is checks or
// as casts, since the web compilers should return true and succeed for all JS
// types.
var jsExtendJS = JSExtendJSClass(1, 2);
expect(jsExtendJS.a, 1);
expect(jsExtendJS.b, 2);
expect(jsExtendJS.getA(), 1);
expect(jsExtendJS.getB(), 2);
// Test method overrides.
expect(jsExtendJS.getAOrB(), 2);
expect((jsExtendJS as JSClass).getAOrB(), 2);
var jsExtendAnon = JSExtendAnonymousClass(1, 2);
expect(jsExtendAnon.a, 1);
expect(jsExtendAnon.b, 2);
expect(jsExtendAnon.getA(), 1);
expect(jsExtendAnon.getB(), 2);
expect(anonExtendAnon.a, 1);
expect(anonExtendAnon.b, 2);
expect(anonExtendAnon.getA(), 1);
expect(anonExtendAnon.getB(), 2);
expect(anonExtendJS.a, 1);
expect(anonExtendJS.b, 2);
expect(anonExtendJS.getA(), 1);
expect(anonExtendJS.getB(), 2);
expect(anonExtendJS.getAOrB(), 2);
expect((anonExtendJS as JSClass).getAOrB(), 2);
}
void testSubtyping() {
// Test subtyping for inheritance between JS and anonymous classes.
expect(useJSClass is void Function(JSExtendJSClass js), true);
expect(useAnonymousClass is void Function(AnonymousExtendAnonymousClass a),
true);
expect(useJSClass is void Function(AnonymousExtendJSClass a), true);
expect(useAnonymousClass is void Function(JSExtendAnonymousClass js), true);
expect(useJSClass is void Function(AnonymousExtendAnonymousClass a), false);
expect(useAnonymousClass is void Function(JSExtendJSClass js), false);
expect(useJSClass is void Function(JSExtendAnonymousClass js), false);
expect(useAnonymousClass is void Function(AnonymousExtendJSClass a), false);
}