| // 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); |
| } |