| /* |
| * Copyright 2016 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.core.interop; |
| |
| import com.google.gwt.junit.client.GWTTestCase; |
| |
| import jsinterop.annotations.JsMethod; |
| import jsinterop.annotations.JsPackage; |
| import jsinterop.annotations.JsType; |
| |
| /** |
| * Tests JsType object method devirtualization functionality. |
| */ |
| @SuppressWarnings("cast") |
| public class JsTypeObjectMethodsTest extends GWTTestCase { |
| |
| @Override |
| public String getModuleName() { |
| return "com.google.gwt.core.Interop"; |
| } |
| |
| @JsType(isNative = true) |
| interface NativeObject { |
| } |
| |
| public native NativeObject createWithEqualsAndHashCode(int a, int b) /*-{ |
| return {a : a, b : b, hashCode: function() { return this.b }, equals : |
| function(other) { return this.a == other.a; } }; |
| }-*/; |
| |
| public native NativeObject createWithoutEqualsAndHashCode(int a, int b) /*-{ |
| return {a : a, b : b} ; |
| }-*/; |
| |
| @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object") |
| static class NativeClassWithHashCode { |
| public native int hashCode(); |
| } |
| |
| static class SubclassNativeClassWithHashCode extends NativeClassWithHashCode { |
| private int n; |
| |
| public SubclassNativeClassWithHashCode(int n) { |
| this.n = n; |
| } |
| |
| @JsMethod |
| public int hashCode() { |
| return n; |
| } |
| } |
| static class ImplementsNativeObject implements NativeObject { |
| private int n; |
| public ImplementsNativeObject(int n) { |
| this.n = n; |
| } |
| @JsMethod |
| public int hashCode() { |
| return n; |
| } |
| } |
| |
| public void testHashCode() { |
| assertEquals(3, createWithEqualsAndHashCode(1, 3).hashCode()); |
| NativeObject o1 = createWithoutEqualsAndHashCode(1, 3); |
| NativeObject o2 = createWithoutEqualsAndHashCode(1, 3); |
| assertTrue(o1.hashCode() != o2.hashCode()); |
| assertTrue(((Object) o1).hashCode() != ((Object) o2).hashCode()); |
| assertEquals(8, new SubclassNativeClassWithHashCode(8).hashCode()); |
| assertEquals(8, ((Object) new SubclassNativeClassWithHashCode(8)).hashCode()); |
| assertEquals(9, ((Object) new ImplementsNativeObject(9)).hashCode()); |
| assertEquals(10, callHashCode(new SubclassNativeClassWithHashCode(10))); |
| } |
| |
| public void testEquals() { |
| assertEquals(createWithEqualsAndHashCode(1, 3), createWithEqualsAndHashCode(1, 4)); |
| NativeObject o1 = createWithoutEqualsAndHashCode(1, 3); |
| NativeObject o2 = createWithoutEqualsAndHashCode(1, 3); |
| assertTrue(createWithEqualsAndHashCode(1, 3).equals(createWithoutEqualsAndHashCode(1, 4))); |
| assertTrue(((Object) createWithEqualsAndHashCode(1, 3)).equals(createWithoutEqualsAndHashCode(1, 4))); |
| assertFalse(createWithoutEqualsAndHashCode(1, 4).equals(createWithEqualsAndHashCode(1, 3))); |
| assertFalse(((Object) createWithoutEqualsAndHashCode(1, 4)).equals(createWithEqualsAndHashCode(1, 3))); |
| assertFalse(o1.equals(o2)); |
| assertFalse(((Object) o1).equals(o2)); |
| } |
| |
| private native int callHashCode(Object obj) /*-{ |
| return obj.hashCode(); |
| }-*/; |
| |
| // Use an existing class for native subclassing tests to work around the need of injecting a |
| // JS class before the subclass definitions |
| @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Error") |
| private static class MyNativeError { |
| MyNativeError() { } |
| MyNativeError(String error) { } |
| public native int hashCode(); |
| public int myValue; |
| } |
| |
| private static class MyNativeErrorSubtype extends MyNativeError { |
| MyNativeErrorSubtype(int n) { |
| myValue = n; |
| } |
| |
| public String toString() { |
| return "(Sub)myValue: " + myValue; |
| } |
| } |
| |
| private static MyNativeError createMyNativeError(int n) { |
| MyNativeError error = new MyNativeError("(Error)myValue: " + n); |
| error.myValue = n; |
| return error; |
| } |
| |
| public void testJavaLangObjectMethodsOrNativeSubtypes() { |
| patchErrorWithJavaLangObjectMethods(); |
| assertEquals(createMyNativeError(3), createMyNativeError(3)); |
| assertFalse(createMyNativeError(3).equals(createMyNativeError(4))); |
| |
| assertEquals(createMyNativeError(6), new MyNativeErrorSubtype(6)); |
| assertTrue(createMyNativeError(6).toString().contains("(Error)myValue: 6")); |
| assertEquals("(Sub)myValue: 6", new MyNativeErrorSubtype(6).toString()); |
| |
| // Tests that hashcode is actually working through the object trampoline and |
| // assumes that consecutive hashchodes are different. |
| assertFalse(createMyNativeError(3).hashCode() == new MyNativeError().hashCode()); |
| } |
| |
| private static native void patchErrorWithJavaLangObjectMethods() /*-{ |
| $wnd.Error.prototype.equals = function (o) { |
| return this.myValue == o.myValue; |
| }; |
| }-*/; |
| } |