blob: e39e1b966cc24a8285d2e235566136414c2e3953 [file] [log] [blame]
// 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);
}