| // Copyright (c) 2013, 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. | |
| import "native_testing.dart"; | |
| // Test for dartNativeDispatchHooksTransformer | |
| // - uncached, instance, leaf and interior caching modes. | |
| // - composition of getTag. | |
| @Native("T1A") | |
| class T1A { | |
| foo() native; | |
| } | |
| @Native("T1B") | |
| class T1B { | |
| foo() native; | |
| } | |
| @Native("T1C") | |
| class T1C { | |
| foo() native; | |
| } | |
| @Native("T1D") | |
| class T1D { | |
| foo() native; | |
| } | |
| makeT1A() native; | |
| makeT1B() native; | |
| makeT1C() native; | |
| makeT1D() native; | |
| int getTagCallCount() native; | |
| void clearTagCallCount() native; | |
| void setup() { | |
| JS('', r''' | |
| (function(){ | |
| function T1A() { this.v = "a"; } | |
| function T1B() { this.v = "b"; } | |
| function T1C() { this.v = "c"; } | |
| function T1D() { this.v = "d"; } | |
| // T1B, T1C and T1D extend T1A in the implementation but not the declared types. | |
| // T1A must not be leaf-cached otherwise we will pick up the interceptor for T1A | |
| // when looking for the dispatch record for an uncached or not-yet-cached T1B, | |
| // T1C or T1D. | |
| Object.setPrototypeOf(T1B.prototype, T1A.prototype); | |
| Object.setPrototypeOf(T1C.prototype, T1A.prototype); | |
| Object.setPrototypeOf(T1D.prototype, T1A.prototype); | |
| // All classes share one implementation of native method 'foo'. | |
| T1A.prototype.foo = function() { return this.v + this.name(); }; | |
| T1A.prototype.name = function() { return "A"; }; | |
| T1B.prototype.name = function() { return "B"; }; | |
| T1C.prototype.name = function() { return "C"; }; | |
| T1D.prototype.name = function() { return "D"; }; | |
| self.makeT1A = function(){return new T1A()}; | |
| self.makeT1B = function(){return new T1B()}; | |
| self.makeT1C = function(){return new T1C()}; | |
| self.makeT1D = function(){return new T1D()}; | |
| self.nativeConstructor(T1A, undefined, true); | |
| self.nativeConstructor(T1B, undefined, true); | |
| self.nativeConstructor(T1C, undefined, true); | |
| self.nativeConstructor(T1D, undefined, true); | |
| var getTagCount = 0; | |
| self.getTagCallCount = function() { return getTagCount; }; | |
| self.clearTagCallCount = function() { getTagCount = 0; }; | |
| function transformer1(hooks) { | |
| var getTag = hooks.getTag; | |
| function getTagNew(obj) { | |
| var tag = getTag(obj); | |
| // Dependency to test composition, rename T1D -> Dep -> -T1D. | |
| if (tag == "T1D") return "Dep"; | |
| return tag; | |
| } | |
| hooks.getTag = getTagNew; | |
| } | |
| function transformer2(hooks) { | |
| var getTag = hooks.getTag; | |
| function getTagNew(obj) { | |
| ++getTagCount; | |
| var tag = getTag(obj); | |
| if (tag == "T1A") return "+T1A"; // Interior cached on prototype | |
| if (tag == "T1B") return "~T1B"; // Uncached | |
| if (tag == "T1C") return "!T1C"; // Instance cached | |
| if (tag == "Dep") return "-T1D"; // Leaf cached on prototype | |
| return tag; | |
| } | |
| hooks.getTag = getTagNew; | |
| } | |
| self.dartNativeDispatchHooksTransformer = [transformer1, transformer2]; | |
| })()'''); | |
| applyTestExtensions(['T1A', 'T1B', 'T1C', 'T1D']); | |
| } | |
| main() { | |
| nativeTesting(); | |
| setup(); | |
| var t1a = makeT1A(); | |
| var t1b = makeT1B(); | |
| var t1c = makeT1C(); | |
| var t1d = makeT1D(); | |
| clearTagCallCount(); | |
| Expect.equals("aA", confuse(t1a).foo(), 't1a is T1A'); | |
| Expect.equals("bB", confuse(t1b).foo(), 't1b is T1B'); | |
| Expect.equals("cC", confuse(t1c).foo(), 't1c is T1C'); | |
| Expect.equals("dD", confuse(t1d).foo(), 't1d is T1D'); | |
| Expect.equals(4, getTagCallCount(), '4 fresh instances / types'); | |
| clearTagCallCount(); | |
| Expect.equals("aA", confuse(t1a).foo(), 't1a is T1A'); | |
| Expect.equals("bB", confuse(t1b).foo(), 't1b is T1B'); | |
| Expect.equals("cC", confuse(t1c).foo(), 't1c is T1C'); | |
| Expect.equals("dD", confuse(t1d).foo(), 't1d is T1D'); | |
| Expect.equals(1, getTagCallCount(), '1 = 1 uncached + (3 cached)'); | |
| t1a = makeT1A(); | |
| t1b = makeT1B(); | |
| t1c = makeT1C(); | |
| t1d = makeT1D(); | |
| clearTagCallCount(); | |
| Expect.equals("aA", confuse(t1a).foo(), 't1a is T1A'); | |
| Expect.equals("bB", confuse(t1b).foo(), 't1b is T1B'); | |
| Expect.equals("cC", confuse(t1c).foo(), 't1c is T1C'); | |
| Expect.equals("dD", confuse(t1d).foo(), 't1d is T1D'); | |
| Expect.equals( | |
| 2, | |
| getTagCallCount(), | |
| '2 = 1 fresh instance + 1 uncached (+ 2 proto cached)', | |
| ); | |
| clearTagCallCount(); | |
| Expect.equals("aA", confuse(t1a).foo(), 't1a is T1A'); | |
| Expect.equals("bB", confuse(t1b).foo(), 't1b is T1B'); | |
| Expect.equals("cC", confuse(t1c).foo(), 't1c is T1C'); | |
| Expect.equals("dD", confuse(t1d).foo(), 't1d is T1D'); | |
| Expect.equals( | |
| 1, | |
| getTagCallCount(), | |
| '1 = 2 proto cached + 1 instance cached + 1 uncached', | |
| ); | |
| } |