/* * Copyright 2013 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.dev.jjs.test; import com.google.gwt.dev.jjs.test.overrides.package1.Caller; import com.google.gwt.dev.jjs.test.overrides.package1.ClassExposingM; import com.google.gwt.dev.jjs.test.overrides.package1.SomeParent; import com.google.gwt.dev.jjs.test.overrides.package1.SomeParentParent; import com.google.gwt.dev.jjs.test.overrides.package1.SomeParentParentParent; import com.google.gwt.dev.jjs.test.overrides.package1.SubClassExposingM; import com.google.gwt.dev.jjs.test.overrides.package2.SomeSubClassInAnotherPackage; import com.google.gwt.dev.jjs.test.overrides.package2.SomeSubSubClassInAnotherPackage; import com.google.gwt.dev.jjs.test.overrides.package3.SomeInterface; import com.google.gwt.dev.jjs.test.overrides.package3.SomePackageConfusedParent; import com.google.gwt.junit.DoNotRunWith; import com.google.gwt.junit.Platform; import com.google.gwt.junit.client.GWTTestCase; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javaemul.internal.annotations.DoNotInline; import jsinterop.annotations.JsMethod; import jsinterop.annotations.JsPackage; import jsinterop.annotations.JsProperty; import jsinterop.annotations.JsType; /** * Tests Miscelaneous fixes. */ public class CompilerMiscRegressionTest extends GWTTestCase { @Override public String getModuleName() { return "com.google.gwt.dev.jjs.CompilerSuite"; } native double toNumber(String value) /*-{ return +value; }-*/; native double addAndConvert(double v1, String v2) /*-{ return v1 + +v2; }-*/; native double minusAndDecrement(double val) /*-{ var lhs = val; return - --lhs; }-*/; /** * The array {@code map.get("one")[0]} gets normalized (by {@link ImplementCastsAndTypeChecks}) to * {@code Cast.dynamicCast(map.get("one"), ...)[0]}. The expression resulting from dynamiCast * would have type Object and that would not be a valid type for an array access operation. */ public void testOverridingReturnType() { Map<String, String[]> map = new HashMap(); map.put("one", new String[10]); map.get("one")[0] = "one"; assertEquals("one", map.get("one")[0]); } /** * Test for issues 6373 and 3942. */ public void testUnaryPlus() { // With the unary + operator stripped the first assertion only fails in // dev mode, in web mode the comparison made by assertEquals masks // the error; whereas the second fails in both dev and web modes. assertEquals(11.0, toNumber("11")); assertEquals(12.0, toNumber("10") + toNumber("2")); assertEquals(12.0, addAndConvert(10, "2")); assertEquals(-10.0, minusAndDecrement(11)); } private static float[] copy(float[] src, float[] dest) { System.arraycopy(src, 0, dest, 0, Math.min(src.length, dest.length)); return dest; } private void throwE(String message) { throw new RuntimeException(message); } /** * Test for issue 8243. */ public void testAddAllLargeNumberOfElements() { int dstLength = 10; // Some browser have a limit on the number of parameters a function can have and 130000 barely // exceeds Chrome limit (as of V34). // This limit also applies when functions are called through apply(). int srcLength = 130000; List<String> original = new ArrayList<String>(); for (int i = 0; i < dstLength; i++) { original.add("foo"); } List<String> src = new ArrayList<String>(); for (int i = 0; i < srcLength; i++) { src.add("bar"); } original.addAll(src); final int totalLength = srcLength + dstLength; assertEquals(totalLength, original.size()); // Check the result sampling as iterating through large arrays seems costly in IE. for (int i = 0; i < totalLength; i += 1000) { if (i < dstLength) { assertEquals("foo", original.get(i)); } else { assertEquals("bar", original.get(i)); } } } /** * Test for issue 7253. */ public void testNestedTryFollowedByTry() { try { throwE("1"); fail("Should have thrown RuntimeException"); } catch (RuntimeException e) { assertEquals("1", e.getMessage()); try { throwE("2"); fail("Should have thrown RuntimeException"); } catch (RuntimeException e2) { assertEquals("2", e2.getMessage()); } } try { throwE("3"); fail("Should have thrown RuntimeException"); } catch (RuntimeException e) { assertEquals("3", e.getMessage()); } } /** * Test for issue 6638. */ public void testNewArrayInlining() { float[] src = new float[]{1,1,1}; float[] dest = copy(src, new float[3]); assertEqualContents(src, dest); } /** * Tests complex overriding patterns involving package private methods. * <p> * Test for issue 8654. */ public void testOverride() { Caller aCaller = new Caller(); assertEquals("SomeParentParent", aCaller.callPackagePrivatem(new SomeParentParent())); assertEquals("SomeParent", aCaller.callPackagePrivatem(new SomeParent())); assertEquals("SomeParent", aCaller.callPackagePrivatem( new SomeSubClassInAnotherPackage())); assertEquals("SomeSubClassInAnotherPackage", SomeSubClassInAnotherPackage.pleaseCallm(new SomeSubClassInAnotherPackage())); assertEquals("SomeSubSubClassInAnotherPackage", SomeSubClassInAnotherPackage.pleaseCallm(new SomeSubSubClassInAnotherPackage())); assertEquals("ClassExposingM", aCaller.callPackagePrivatem(new ClassExposingM())); SomeInterface i = new ClassExposingM(); assertEquals("ClassExposingM", i.m()); assertEquals("live at ClassExposingM", new ClassExposingM().f()); // Confirm that both calling m through SomeInterface and through SomeParentParentParent // dispatch to the right implementation. SomeInterface i1 = new SubClassExposingM(); assertEquals("SubClassExposingM", i1.m()); assertEquals("SubClassExposingM", SomeParentParentParent.callSomeParentParentParentM(new SubClassExposingM())); assertEquals("SomeParentParentParent", SomeParentParentParent.callSomeParentParentParentM(new SomeParentParentParent())); assertEquals("SomeParentParentParent", SomeParentParentParent.callSomeParentParentParentM(new SomePackageConfusedParent())); assertEquals("SomeParentParent", SomeParentParentParent.callSomeParentParentParentM(new SomeParentParent())); assertEquals("SomeParent", SomeParentParentParent.callSomeParentParentParentM(new SomeParent())); assertEquals("SomeParent", SomeParentParentParent.callSomeParentParentParentM(new SomeSubClassInAnotherPackage())); assertEquals("SomeParent", SomeParentParentParent.callSomeParentParentParentM(new SomeSubSubClassInAnotherPackage())); } enum MyEnum { A, B, C; public final static MyEnum[] VALUES = values(); public int getPriority() { return VALUES.length - ordinal(); } } /** * Tests that enum ordinalizer does not incorrectly optimize {@code MyEnum}. * <p> * Test for issue 8846. */ public void testMyEnum() { assertEquals(2, MyEnum.B.getPriority()); } enum OrderingProblem { A, B; public static OrderingProblem getPriority1() { if (new Integer(1).toString().isEmpty()) { return B; } return A; } } /** * Test for regression introduced in patch https://gwt-review.googlesource.com/#/c/9083; where * depending on the order in which references to the enum class were encountered, some instances * were not correctly replaced . */ public void testOrderingProblem() { assertEquals(OrderingProblem.A.ordinal(), OrderingProblem.getPriority1().ordinal()); } /** * Tests that regexes are not incorrectly internalized. * * Test for issue 8865. */ public native void testJavaScriptRegExps() /*-{ // Make regexes large enough so that the will be interned (if regex interning was enabled). var regExp1 = /this is a string where the search/g; var regExp2 = /this is a string where the search/g; var str = "this is a string where the search occurs"; @junit.framework.Assert::assertEquals(ZZ)( regExp1.test(str), regExp2.test(str)); }-*/; private static final double MINUTES_IN_DAY = 24 * 60; @DoNotInline public void assertStaticEvaluationRegression(int hour, int minute) { // Do not inline this method so that the problematic expression reaches JsStaticEval. double expected = hour * 60 + minute; expected /= MINUTES_IN_DAY; expected *= 100; assertEquals(expected , (hour * 60 + minute) / MINUTES_IN_DAY * 100); } /** * Test for issue 8934. */ public void testStaticEvaluationRegression() { // Perform two calls with different constant values to make sure the assertStaticEvaluation does // not get the constant parameters propagated and statically evaluated in the Java AST. assertStaticEvaluationRegression(10, 20); assertStaticEvaluationRegression(20, 10); } /** * Test for issue 8909. * <p> * DevMode does not conform to JS arithmetic semantics and this method tests exactly that. */ @DoNotRunWith(Platform.Devel) public void testStaticEvaluationSematics() { float num = getRoundedValue(1.005f); assertEquals(1.00, num, 0.001); } private float getRoundedValue(float parameter) { float local = parameter; local = local * 100f; return Math.round(local) / 100f; } /** * Test for issue 9043. */ public native void testMultipleClassLiteralReferences() /*-{ var a = @com.google.gwt.dev.jjs.test.CompilerMiscRegressionTest::class; var b = @com.google.gwt.dev.jjs.test.CompilerMiscRegressionTest::class; }-*/; /** * Test for issue 9153. * <p> * Typetightener used to incorrectly tighten method calls marked with STATIC_DISPATCH_ONLY. */ public void testIncorrectDispatch() { state = new int[1]; new B().m(); assertEquals(1, state[0]); } static int[] state; @JsType abstract static class A { public void m() { state[0] = 1; } } @JsType static class B extends A { public void m() { super.m(); } } private static void assertEqualContents(float[] expected, float[] actual) { assertEquals("Array length mismatch", expected.length, actual.length); for (int i = 0; i < expected.length; i++) { assertEquals("Array mismatch at element " + i , expected[i], actual[i]); } } @JsType(isNative = true) interface SomeNativeInterface { @JsProperty String getTextContent(); } @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object") static abstract class AbstractNativeType implements SomeNativeInterface { } private static native AbstractNativeType createAbstractNativeType(String value) /*-{ return {textContent : value}; }-*/; // Tests that methods that override native JsMethods are generated properly w.r.t // JsMethod/JsProperty annotations even if the overridden method is not live. // See bug #9358 // // Make sure this method is part of a suite that is run with -nogenerateJsInteropExports. public void testNativeJsMethodDispatch_unreferencedSupertypeMethod() { final AbstractNativeType o = createAbstractNativeType("Hello"); assertEquals("Hello", o.getTextContent()); } @JsMethod private static List<String> singletonFrom(int i, String... arguments) { // Make the second parameter varargs and pass it as a whole to trigger the arguments copying // preamble. return Arrays.asList(arguments).subList(i,i + 1); } @JsMethod private static List<String> argumentsParameterClasher(int arguments, String... others) { // Make the second parameter varargs and pass it as a whole to trigger the arguments copying // preamble. return Arrays.asList(others).subList(0, arguments); } @JsMethod private static List<String> argumentsVariableClasher(int i, String... others) { // Make the second parameter varargs and pass it as a whole to trigger the arguments copying // preamble. { int arguments = 3; } return Arrays.asList(others).subList(0, i); } public void testVarargsNamedArguments() { assertEquals("GoodBye", singletonFrom(1, "Hello", "GoodBye").get(0)); assertEquals("Hello", argumentsParameterClasher(1, "Hello", "GoodBye").get(0)); assertEquals("Hello", argumentsVariableClasher(1, "Hello", "GoodBye").get(0)); } @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Array") private static class NativeArray { @JsProperty(name = "length") public int len; @JsMethod(name = "push") public native void add(double element); } private static native Object newArray() /*-{ return @NativeArray::new()(); }-*/; private static native int arrayLength(Object array) /*-{ return array.@NativeArray::len; }-*/; private static native void arrayAdd(Object array, double d) /*-{ array.@NativeArray::add(D)(d); return array; }-*/; private static native Object newArrayThroughCtorReference() /*-{ var ctor = @NativeArray::new(); return ctor(); }-*/; private static native boolean isNan(double number) /*-{ return @Global::isNan(D)(number); }-*/; private static native double getNan() /*-{ return @Global::Nan; }-*/; @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object") private static class Global { @JsProperty(namespace = JsPackage.GLOBAL, name = "NaN") public static double Nan; @JsMethod(namespace = JsPackage.GLOBAL, name = "isNaN") public static native boolean isNan(double number); } // Regression tests for issue #9520. public void testNativeConstructorJSNI() { Object nativeArray = newArray(); arrayAdd(nativeArray, 0); arrayAdd(nativeArray, 1); assertTrue(nativeArray instanceof NativeArray); assertEquals(2, arrayLength(nativeArray)); assertTrue(newArrayThroughCtorReference() instanceof NativeArray); assertTrue(Double.isNaN(getNan())); assertTrue(isNan(Double.NaN)); } }