blob: f9bb6b92cd7412cc91f0b3802c5ea586eccad7e6 [file] [log] [blame]
/*
* Copyright 2008 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.core.client.JavaScriptException;
import com.google.gwt.dev.jjs.test.compilertests.MethodNamedSameAsClass;
import com.google.gwt.junit.client.GWTTestCase;
import junit.framework.Assert;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.Locale;
/**
* Miscellaneous tests of the Java to JavaScript compiler.
*/
@SuppressWarnings("unused")
public class CompilerTest extends GWTTestCase {
interface MyMap {
Object get(String key);
}
interface Silly { }
interface SillyComparable<T extends Silly> extends Comparable<T> {
@Override
int compareTo(T obj);
}
private abstract static class AbstractSuper {
public static String foo() {
if (FALSE) {
// prevent inlining
return foo();
}
return "AbstractSuper";
}
}
private abstract static class Apple implements Fruit {
}
private abstract static class Bm2BaseEvent {
}
private abstract static class Bm2ComponentEvent extends Bm2BaseEvent {
}
private static class Bm2KeyNav<E extends Bm2ComponentEvent> implements
Bm2Listener<E> {
@Override
public int handleEvent(Bm2ComponentEvent ce) {
return 5;
}
}
private interface Bm2Listener<E extends Bm2BaseEvent> extends EventListener {
int handleEvent(E be);
}
/**
* Used in {@link #testSwitchOnEnumTypedThis()}.
*/
private static enum ChangeDirection {
NEGATIVE, POSITIVE;
public String getText() {
switch (this) {
case POSITIVE:
return "POSITIVE";
case NEGATIVE:
return "NEGATIVE";
default:
throw new IllegalArgumentException("Unhandled change direction: "
+ this);
}
}
}
private static class ConcreteSub extends AbstractSuper {
public static String foo() {
if (FALSE) {
// prevent inlining
return foo();
}
return "ConcreteSub";
}
}
private static interface Fruit {
}
private static class Fuji extends Apple {
}
private static class Granny extends Apple {
}
private static class NonSideEffectCauser {
public static final String NOT_A_COMPILE_TIME_CONSTANT = null;
}
private static class SideEffectCauser {
private static Object instance = new Object();
static {
CompilerTest.sideEffectChecker++;
}
public static Object causeClinitSideEffect() {
return instance;
}
}
private static class SideEffectCauser2 {
static {
CompilerTest.sideEffectChecker++;
}
public static Object causeClinitSideEffect() {
return null;
}
}
private static class SideEffectCauser3 {
static {
CompilerTest.sideEffectChecker++;
}
public static void causeClinitSideEffect() {
}
}
private static class SideEffectCauser4 {
public static String causeClinitSideEffectOnRead = "foo";
static {
CompilerTest.sideEffectChecker++;
}
}
private static class SideEffectCauser5 {
public static String causeClinitSideEffectOnRead = "bar";
static {
CompilerTest.sideEffectChecker++;
}
}
private static class SideEffectCauser6 extends SideEffectCauser6Super {
public static String causeClinitSideEffectOnRead = "bar";
}
private static class SideEffectCauser6Super {
static {
CompilerTest.sideEffectChecker++;
}
}
/**
* Ensures that a superclass's clinit is run before supercall arguments are
* evaluated.
*/
private static class SideEffectCauser7 extends SideEffectCauser7Super {
public SideEffectCauser7() {
super(SideEffectCauser7Super.SHOULD_BE_TRUE);
}
}
private static class SideEffectCauser7Super {
public static boolean SHOULD_BE_TRUE = false;
static {
SHOULD_BE_TRUE = true;
}
public SideEffectCauser7Super(boolean should_be_true) {
if (should_be_true) {
CompilerTest.sideEffectChecker++;
}
}
}
/**
* Used in test {@link #testPrivateOverride()}.
*/
private static class TpoChild extends TpoParent {
@Override
public int foo() {
return callSuper();
}
private int callSuper() {
return super.foo();
}
}
/**
* Used in test {@link #testPrivateOverride()}.
*/
private static class TpoGrandparent {
public int foo() {
return 0;
}
}
/**
* Used in test {@link #testPrivateOverride()}.
*/
private static class TpoJsniChild extends TpoJsniParent {
@Override
public native int foo() /*-{
return this.@com.google.gwt.dev.jjs.test.CompilerTest$TpoJsniChild::callSuper()();
}-*/;
private int callSuper() {
return super.foo();
}
}
/**
* Used in test {@link #testPrivateOverride()}.
*/
private static class TpoJsniGrandparent {
public int foo() {
return 0;
}
}
/**
* Used in test {@link #testPrivateOverride()}.
*/
private static class TpoJsniParent extends TpoJsniGrandparent {
@Override
public native int foo() /*-{
// This should call callSuper in TpoJsniParent, not the one
// in TpoJsniChild
return this.@com.google.gwt.dev.jjs.test.CompilerTest$TpoJsniParent::callSuper()();
}-*/;
private int callSuper() {
return super.foo();
}
}
/**
* Used in test {@link #testPrivateOverride()}.
*/
private static class TpoParent extends TpoGrandparent {
@Override
public int foo() {
// This should call the callSuper in TpoJsniParent, not the one
// in TpoJsniChild
return callSuper();
}
private int callSuper() {
return super.foo();
}
}
private static final class UninstantiableType {
public Object field;
public int intField;
private UninstantiableType() {
}
public int returnInt() {
return intField;
}
public Object returnNull() {
return null;
}
}
private static volatile boolean FALSE = false;
private static int sideEffectChecker;
private static volatile int THREE = 3;
private static volatile boolean TRUE = true;
private static volatile boolean volatileBoolean;
private static volatile int volatileInt;
private static volatile long volatileLong;
private static volatile UninstantiableType volatileUninstantiableType;
private static native void accessUninstantiableField(UninstantiableType u) /*-{
u.@com.google.gwt.dev.jjs.test.CompilerTest$UninstantiableType::field.toString();
}-*/;
private static native void accessUninstantiableMethod(UninstantiableType u) /*-{
u.@com.google.gwt.dev.jjs.test.CompilerTest$UninstantiableType::returnNull()();
}-*/;
private static String barShouldInline() {
return "bar";
}
private static void foo(String string) {
}
private static void foo(Throwable throwable) {
}
private static native String jsniReadSideEffectCauser5() /*-{
return @com.google.gwt.dev.jjs.test.CompilerTest$SideEffectCauser5::causeClinitSideEffectOnRead;
}-*/;
private Integer boxedInteger = 0;
@Override
public String getModuleName() {
return "com.google.gwt.dev.jjs.CompilerSuite";
}
public void testArrayAccessSideEffect() {
if (System.getProperty("user.agent", "safari").equals("gecko1_8")) {
// Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1259605
return;
}
int index = 1;
int[] array = null;
try {
// index should be set before exception is thrown
array[index = 2]++;
fail("null reference expected");
} catch (Exception e) {
// expected
}
assertEquals(2, index);
}
public void testArrayStore() {
Object[][] oaa;
oaa = new Object[4][4];
oaa[0][0] = "foo";
assertEquals("foo", oaa[0][0]);
oaa = new Object[4][];
oaa[0] = new Object[4];
oaa[0][0] = "bar";
assertEquals("bar", oaa[0][0]);
Apple[] apple = TRUE ? new Granny[3] : new Apple[3];
Apple g = TRUE ? (Apple) new Granny() : (Apple) new Fuji();
Apple a = apple[0] = g;
assertEquals(g, a);
byte[] bytes = new byte[10];
bytes[0] = (byte) '1';
assertEquals(49, bytes[0]);
Object[] disguisedApple = apple;
expectArrayStoreException(disguisedApple, "Hello");
expectArrayStoreException(disguisedApple, true);
expectArrayStoreException(disguisedApple, 42.0);
}
private void expectArrayStoreException(Object[] array, Object o) {
try {
array[0] = o;
fail("Expected ArrayStoreException");
} catch (ArrayStoreException e) {
}
}
public void testUnboxedArrays() {
Double[] doubleArray = new Double[1];
doubleArray[0] = 42.0;
expectArrayStoreException(doubleArray, true);
expectArrayStoreException(doubleArray, "Hello");
expectArrayStoreException(doubleArray, new Integer(23));
Boolean[] booleanArray = new Boolean[1];
booleanArray[0] = true;
expectArrayStoreException(booleanArray, 42.0);
expectArrayStoreException(booleanArray, "Hello");
expectArrayStoreException(booleanArray, new Integer(23));
String[] stringArray = new String[1];
stringArray[0] = "Hello";
expectArrayStoreException(stringArray, 42.0);
expectArrayStoreException(stringArray, true);
expectArrayStoreException(stringArray, new Integer(23));
}
/**
* Issue 3064: when the implementation of an interface comes from a
* superclass, it can be necessary to add a bridge method that overrides the
* interface method and calls the inherited method.
*/
public void testBridgeMethods1() {
abstract class AbstractFoo {
public int compareTo(AbstractFoo o) {
return 0;
}
}
class MyFoo extends AbstractFoo implements Comparable<AbstractFoo> {
}
/*
* This subclass adds an extra curve ball: only one bridge method should be
* created, in class MyFoo. MyFooSub should not get its own but instead use
* the inherited one. Otherwise, two final methods with identical signatures
* would override each other.
*/
class MyFooSub extends MyFoo {
}
Comparable<AbstractFoo> comparable1 = new MyFooSub();
assertEquals(0, comparable1.compareTo(new MyFoo()));
Comparable<AbstractFoo> comparable2 = new MyFoo();
assertEquals(0, comparable2.compareTo(new MyFooSub()));
}
/**
* Issue 3304. Doing superclasses first is tricky when the superclass is a raw
* type.
*/
@SuppressWarnings("unchecked")
public void testBridgeMethods2() {
Bm2KeyNav<?> obs = new Bm2KeyNav() {
};
assertEquals(5, obs.handleEvent(null));
}
/**
* When adding a bridge method, be sure to handle transitive overrides
* relationships.
*/
public void testBridgeMethods3() {
class AbstractFoo implements Silly {
public int compareTo(AbstractFoo obj) {
if (FALSE) {
return compareTo(obj);
}
return 0;
}
}
class MyFoo extends AbstractFoo implements SillyComparable<AbstractFoo> {
}
assertEquals(0, new MyFoo().compareTo(new MyFoo()));
}
/**
* Issue 3517. Sometimes JDT adds a bridge method when a subclass's method
* differs only by return type. In some versions of GWT, this has resulted in
* a bridge method overriding its own target, and eventually TypeTightener
* producing an infinite recursion.
*/
public void testBridgeMethods4() {
abstract class MyMapAbstract<V> implements MyMap {
@Override
public String get(String key) {
return null;
}
}
final class MyMapImpl<V> extends MyMapAbstract<V> {
}
MyMapImpl<String> mmap = new MyMapImpl<String>();
assertNull(mmap.get("foo"));
}
public void testCastOptimizer() {
Granny g = new Granny();
Apple a = g;
Fruit f = g;
a = (Apple) f;
g = (Granny) a;
g = (Granny) f;
}
public void testClassLiterals() {
if (Object.class.getName().startsWith("Class$")) {
// If class metadata is disabled
return;
}
assertEquals("void", void.class.toString());
assertEquals("int", int.class.toString());
assertEquals("class java.lang.String", String.class.toString());
assertEquals("class com.google.gwt.dev.jjs.test.CompilerTest",
CompilerTest.class.toString());
assertEquals(
"class com.google.gwt.dev.jjs.test.CompilerTest$UninstantiableType",
UninstantiableType.class.toString());
assertEquals("interface com.google.gwt.dev.jjs.test.CompilerTest$Fruit",
Fruit.class.toString());
assertEquals("class [I", int[].class.toString());
assertEquals("class [Ljava.lang.String;", String[].class.toString());
assertEquals("class [Lcom.google.gwt.dev.jjs.test.CompilerTest;",
CompilerTest[].class.toString());
assertEquals(
"class [Lcom.google.gwt.dev.jjs.test.CompilerTest$UninstantiableType;",
UninstantiableType[].class.toString());
assertEquals("class [Lcom.google.gwt.dev.jjs.test.CompilerTest$Fruit;",
Fruit[].class.toString());
}
public void testClinitSideEffectInlining() {
sideEffectChecker = 0;
SideEffectCauser.causeClinitSideEffect();
assertEquals(1, sideEffectChecker);
SideEffectCauser2.causeClinitSideEffect();
assertEquals(2, sideEffectChecker);
SideEffectCauser3.causeClinitSideEffect();
assertEquals(3, sideEffectChecker);
String foo = SideEffectCauser4.causeClinitSideEffectOnRead;
assertEquals(4, sideEffectChecker);
jsniReadSideEffectCauser5();
assertEquals(5, sideEffectChecker);
foo = SideEffectCauser6.causeClinitSideEffectOnRead;
assertEquals(6, sideEffectChecker);
new SideEffectCauser7();
assertEquals(7, sideEffectChecker);
String checkRescued = NonSideEffectCauser.NOT_A_COMPILE_TIME_CONSTANT;
assertEquals(null, checkRescued);
}
private static boolean testClassInitialized = false;
private static class TestClass {
static {
testClassInitialized = true;
}
public static int staticField = 32;
}
public void testClinitOverInstance() {
assertEquals(32, getTest().staticField);
assertTrue(testClassInitialized);
}
private TestClass getTest() {
assertFalse(testClassInitialized);
return null;
}
public void testConditionals() {
assertTrue(TRUE ? TRUE : FALSE);
assertFalse(FALSE ? TRUE : FALSE);
assertFalse(TRUE ? FALSE : TRUE);
assertTrue(FALSE ? FALSE : TRUE);
assertTrue(true ? TRUE : FALSE);
assertFalse(false ? TRUE : FALSE);
assertFalse(true ? FALSE : TRUE);
assertTrue(false ? FALSE : TRUE);
assertTrue(TRUE ? true : FALSE);
assertFalse(FALSE ? true : FALSE);
assertFalse(TRUE ? false : TRUE);
assertTrue(FALSE ? false : TRUE);
assertTrue(TRUE ? TRUE : false);
assertFalse(FALSE ? TRUE : false);
assertFalse(TRUE ? FALSE : true);
assertTrue(FALSE ? FALSE : true);
}
/**
* Test for issue 7045.
*
* Background. GWT does not explicitly model block scopes but allows names across different blocks
* to be repeated and are represented as different local variables with the same name.
*
* The GenerateJavaScriptAST assumes that locals with the same name can be coalesced. This
* assumption only holds if no optimization introduces a local variable reference in
* a blockscope that is using a different local of the same name.
*
* The dataflow optimizer does not respect this assumption when doing copy propagation.
*/
public void testDataflowDuplicateNamesError() {
StringBuilder topScope;
{
StringBuilder localScopeDuplicatedVar = new StringBuilder();
localScopeDuplicatedVar.append("initial text");
topScope = localScopeDuplicatedVar;
}
{
// It's important that this StringBuilder have the same name as the one above.
StringBuilder localScopeDuplicatedVar = new StringBuilder();
localScopeDuplicatedVar.append("different text");
}
{
// The value of main should be "initial text" at this point, but if this code
// is run as compiled JavaScript then it will be "different text". (before the issue was
// fixed).
// The copy propagation optimization in the DataflowOptimizer replaces the reference to
// topScope by localScopeDuplicatedVar (from the previous block).
// When the JavaScript output is produces these two variables are coalesced.
// Although unlikely an error like this can occur due to the way temporary variables are
// generated via the TempLocalVisitor.
assertEquals("initial text", topScope.toString());
}
}
private int functionWithClashingParameters(int i0) {
int c = 0;
for (int i = 0; i < 5; i++) {
c++;
}
for (int i = 0; i < 6; i++) {
c++;
}
// Assumes that GenerateJavaScriptAST will rename one of the "i" variables to i0.
return i0;
}
/**
* Test for issue 8870.
*
*/
public void testParameterVariableClash() {
assertEquals(1, functionWithClashingParameters(1));
}
/**
* Test for issue 8877.
*/
public void testOptimizeInstanceOf() {
class A {
int counter = 0;
Object get() {
if (counter >= 0) {
counter++;
}
return "aString";
}
}
A anA = new A();
assertFalse(anA.get() instanceof Integer);
assertEquals(1, anA.counter);
}
public void testDeadCode() {
while (returnFalse()) {
break;
}
do {
break;
} while (false);
do {
break;
} while (returnFalse());
for (; returnFalse();) {
}
boolean check = false;
for (check = true; returnFalse(); fail()) {
fail();
}
assertTrue(check);
if (returnFalse()) {
fail();
} else {
}
if (!returnFalse()) {
} else {
fail();
}
// For these following tests, make sure that side effects in conditions
// get propagated, even if they cause introduction of dead code.
//
boolean b = false;
if ((b = true) ? true : true) {
}
assertTrue(b);
boolean c = true;
int val = 0;
for (val = 1; c = false; ++val) {
}
assertFalse(c);
boolean d = true;
while (d = false) {
}
assertFalse(d);
boolean e = true;
if (true | (e = false)) {
}
assertFalse(e);
}
/**
* Test that code considered unreachable by the JDT does not crash the GWT
* compiler.
*/
public void testDeadCode2() {
class SillyList {
}
final SillyList outerLocalVariable = new SillyList();
new SillyList() {
private void pretendToUse(SillyList x) {
}
void blah() {
if (true) {
throw new RuntimeException();
}
/*
* This code is unreachable, and so outerLocalVariable is never actually
* read by reachable code.
*/
pretendToUse(outerLocalVariable);
}
};
}
public void testDeadTypes() {
if (false) {
new Object() {
}.toString();
class Foo {
void a() {
}
}
new Foo().a();
}
}
/**
* Make sure that the compiler does not crash itself on user code that divides
* by zero. The actual behavior varies by the numeric type and whether it is
* Development Mode or Production Mode, but the important thing is that the
* compiler does not crash.
*/
@SuppressWarnings({"divzero", "ConstantOverflow"})
public void testDivByZero() {
assertTrue(Double.isNaN(0.0 / 0.0));
try {
// 0 / 0 is currently 0 in Production Mode.
assertEquals(0, 0 / 0);
} catch (ArithmeticException expected) {
// expected in Development Mode
}
try {
volatileLong = 0L / 0;
fail("expected an ArithmeticException");
} catch (ArithmeticException expected) {
// expected
}
assertTrue(Double.isNaN(0.0 % 0.0));
try {
// 0 % 0 is currently NaN in Production Mode.
assertTrue(Double.isNaN(0 % 0));
} catch (ArithmeticException expected) {
// expected in Development Mode
}
try {
volatileLong = 0L % 0;
fail("expected an ArithmeticException");
} catch (ArithmeticException expected) {
// expected
}
}
public void testEmptyBlockStatements() {
boolean b = false;
while (b) {
}
do {
} while (b);
for (; b;) {
}
for (;;) {
break;
}
if (b) {
}
if (b) {
} else {
b = false;
}
if (b) {
} else {
}
}
public native void testEmptyBlockStatementsNative() /*-{
var b = false;
while (b) {
}
do {
} while (b);
for (; b; ) {
}
for (;;) {
break;
}
if (b) {
}
if (b) {
} else {
b = false;
}
if (b) {
} else {
}
}-*/;
// CHECKSTYLE_OFF
@SuppressWarnings("empty")
public void testEmptyStatements() {
boolean b = false;
while (b);
do; while (b);
for (; b;);
for (int i = 0; i < 10; ++i);
for (int i : new int[10]);
for (Integer i : new ArrayList<Integer>());
for (;;)
break;
if (b)
;
if (b)
;
else
b = false;
if (b)
;
else
;
}
// CHECKSTYLE_ON
public native void testEmptyStatementsNative() /*-{
var b = false;
while (b);
do; while (b);
for (; b;);
for (;;)
break;
if (b)
;
if (b)
;
else
b = false;
if (b)
;
else
;
}-*/;
public void testEmptyTryBlock() {
int x = 0;
try {
} finally {
x = 1;
}
assertEquals(1, x);
}
public void testCatchBlockWithKeyword() {
try {
throw new RuntimeException();
} catch (Exception var) {
assertFalse(var.toString().isEmpty());
}
}
/** Ensure that only final fields are initializers when cstrs run, see issue 380. */
public void testFieldInitializationOrder() {
ArrayList<String> seenValues = new ArrayList<String>();
new FieldInitOrderChild(seenValues);
assertEquals("i1=1,i2=0,i3=null,i4=null,i5=1,i6=1,i7=1", seenValues.get(0));
assertEquals("i1=1,i2=1,i3=1,i4=2,i5=1,i6=2,i7=2", seenValues.get(1));
}
public void testForStatement() {
{
int i;
for (i = 0; i < 10; ++i) {
}
assertEquals(10, i);
}
{
int i, c;
for (i = 0, c = 10; i < c; ++i) {
}
assertEquals(10, i);
assertEquals(10, c);
}
{
int j = 0;
for (int i = 0; i < 10; ++i) {
++j;
}
assertEquals(10, j);
}
{
int j = 0;
for (int i = 0, c = 10; i < c; ++i) {
++j;
}
assertEquals(10, j);
}
}
/**
* Issue #615: Internal Compiler Error.
*/
public void testImplicitNull() {
boolean b;
String test = ((((b = true) ? null : null) + " ") + b);
assertTrue(b);
assertEquals("null true", test);
}
/**
* Issue 2886: inlining should cope with local variables that do not have an
* explicit declaration node.
*/
public void testInliningBoxedIncrement() {
// should not actually inline, because it has a temp variable
incrementBoxedInteger();
assertEquals((Integer) 1, boxedInteger);
}
public void testJavaScriptReservedWords() {
boolean delete = TRUE;
for (int in = 0; in < 10; ++in) {
assertTrue(in < 10);
assertTrue(delete);
}
}
public void testLabels() {
int i = 0, j = 0;
outer : for (i = 0; i < 1; ++i) {
inner : for (j = 0; j < 1; ++j) {
break outer;
}
fail();
}
assertEquals(0, i);
assertEquals(0, j);
outer : for (i = 0; i < 1; ++i) {
inner : for (j = 0; j < 1; ++j) {
continue outer;
}
fail();
}
assertEquals(1, i);
assertEquals(0, j);
outer : for (i = 0; i < 1; ++i) {
inner : for (j = 0; j < 1; ++j) {
break inner;
}
}
assertEquals(1, i);
assertEquals(0, j);
outer : for (i = 0; i < 1; ++i) {
inner : for (j = 0; j < 1; ++j) {
continue inner;
}
}
assertEquals(1, i);
assertEquals(1, j);
/*
* Issue 2069: a default with a break should not be stripped unless the
* break has no label.
*/
outer : while (true) {
switch (THREE) {
case 0:
case 1:
break;
default:
break outer;
}
fail("should not be reached");
}
/*
* Issue 2770: labeled breaks in a switch statement with no default case
* should not be pruned.
*/
outer : while (true) {
switch (THREE) {
case 0:
case 1:
case 2:
case 3:
break outer;
case 4:
case 5:
break;
}
}
}
private final class RefObject<T> {
T argvalue;
RefObject(T refarg) {
argvalue = refarg;
}
}
/**
* Test for issue 6471.
*
* Cast normally can not appear on the left hand side of an assignment (they are not allowed in
* Java source). However they might be present in the Java AST as a result of generics and
* erasure; hence they need to be handled in the proper places.
*/
public void testLhsCastString() {
RefObject<String> prefix = new RefObject<String>("Hello");
// Due to erasure, prefix.argvalue is of type Object hence
// the compiler "inserts" a cast transforming it into
// ((String) prefix.argvalue) += ", goodbye!"
// This way a lhs can be a cast (which is illegal in Java source code).
prefix.argvalue += ", goodbye!";
assertEquals("Hello, goodbye!", prefix.argvalue);
}
public void testLhsCastInt() {
RefObject<Integer> prefix = new RefObject<Integer>(41);
// Due to erasure, prefix.argvalue is of type Object hence
// the compiler "inserts" a cast transforming it into
// ((Integer) prefix.argvalue) ++
// This way a lhs can be a cast (which is illegal in Java source code).
prefix.argvalue++;
assertEquals(new Integer(42), prefix.argvalue);
}
public void testLhsCastNested() {
RefObject<Integer> prefix = new RefObject<Integer>(41);
// Due to erasure, prefix.argvalue is of type Object hence
// the compiler "inserts" a cast transforming it into
// ((Integer) prefix.argvalue) ++
// This way a lhs can be a cast (which is illegal in Java source code).
Math.max(prefix.argvalue++,40);
assertEquals(new Integer(42), prefix.argvalue);
}
public void testLocalClasses() {
class Foo {
public Foo(int j) {
assertEquals(1, j);
};
}
final int i;
new Foo(i = 1) {
{
assertEquals(1, i);
}
};
assertEquals(1, i);
}
public void testLocalRefs() {
final String foo = TRUE ? "foo" : "bar";
final String bar = TRUE ? "bar" : "foo";
String result = new Object() {
private String a = foo;
{
a = foo;
}
@Override
public String toString() {
return new Object() {
private static final String constantString = "wallawalla";
private String ai = foo;
{
ai = foo;
}
@SuppressWarnings("ReturnValueIgnored")
@Override
public String toString() {
// this line used to cause ICE due to no synthetic path to bar
bar.valueOf(false);
assertEquals("wallawalla", constantString);
return foo + a + ai;
}
}.toString() + a;
}
}.toString();
assertEquals("foofoofoofoo", result);
}
/**
* test for issue 7824.
*/
public void testMethodNamedSameAsClass() {
MethodNamedSameAsClass obj = new MethodNamedSameAsClass();
obj.MethodNamedSameAsClass();
}
public void testNotOptimizations() {
assertFalse(!true);
assertTrue(!false);
assertTrue(!(TRUE == FALSE));
assertFalse(!(TRUE != FALSE));
assertFalse(!(3 < 4));
assertFalse(!(3 <= 4));
assertTrue(!(3 > 4));
assertTrue(!(3 >= 4));
assertTrue(!(4 < 3));
assertTrue(!(4 <= 3));
assertFalse(!(4 > 3));
assertFalse(!(4 >= 3));
assertTrue(!!TRUE);
assertFalse(!!FALSE);
double nan = Math.random() == 0 ? Double.NaN : Double.NaN;
assertFalse(nan == (0.0 / 0.0));
assertTrue(!(nan > 0));
assertTrue(!(nan < 0));
assertFalse(!!(nan > 0));
}
public void testNullFlow() {
UninstantiableType f = null;
try {
f.returnNull().toString();
fail();
} catch (NullPointerException e) {
// Development Mode
} catch (JavaScriptException e) {
// Production Mode
}
try {
UninstantiableType[] fa = null;
fa[4] = null;
fail();
} catch (NullPointerException e) {
// Development Mode
} catch (JavaScriptException e) {
// Production Mode
}
}
public void testNullFlowArray() {
UninstantiableType[] uta = new UninstantiableType[10];
assertEquals(10, uta.length);
assertEquals(uta[0], null);
uta[1] = null;
assertEquals(uta[1], null);
}
public void testNullFlowOverloads() {
foo((Throwable) null);
foo((String) null);
}
public void testNullFlowVsClassCastPrecedence() {
try {
((UninstantiableType) new Object()).returnNull();
fail();
} catch (ClassCastException e) {
// success
}
}
public void testOuterSuperThisRefs() {
new B();
}
/**
* Test that calling a private instance method does not accidentally call
* another private method that appears to override it. Private methods don't
* truly override each other.
*/
public void testPrivateOverride() {
assertEquals(0, new TpoChild().foo());
assertEquals(0, new TpoJsniChild().foo());
}
public void testReturnStatementInCtor() {
class Foo {
int i;
Foo(int i) {
this.i = i;
if (i == 0) {
return;
} else if (i == 1) {
return;
}
return;
}
}
assertEquals(0, new Foo(0).i);
assertEquals(1, new Foo(1).i);
assertEquals(2, new Foo(2).i);
}
public void testStaticMethodResolution() {
// Issue 2922
assertEquals("AbstractSuper", AbstractSuper.foo());
}
public void testStringOptimizations() {
assertEquals("Herro, AJAX", "Hello, AJAX".replace('l', 'r'));
assertEquals('J', "Hello, AJAX".charAt(8));
assertEquals(11, "Hello, AJAX".length());
assertFalse("Hello, AJAX".equals("me"));
assertTrue("Hello, AJAX".equals("Hello, AJAX"));
assertTrue("Hello, AJAX".equalsIgnoreCase("HELLO, ajax"));
assertEquals("hello, ajax", "Hello, AJAX".toLowerCase(Locale.ROOT));
assertEquals("foobar", "foo" + barShouldInline());
assertEquals("1bar", 1 + barShouldInline());
assertEquals("fbar", 'f' + barShouldInline());
assertEquals("truebar", true + barShouldInline());
assertEquals("3.5bar", 3.5 + barShouldInline());
assertEquals("3.5bar", 3.5f + barShouldInline());
assertEquals("27bar", 27L + barShouldInline());
assertEquals("nullbar", null + barShouldInline());
}
public void testSubclassStaticInnerAndClinitOrdering() {
new CheckSubclassStaticInnerAndClinitOrdering();
}
public void testSwitchOnEnumTypedThis() {
assertEquals("POSITIVE", ChangeDirection.POSITIVE.getText());
assertEquals("NEGATIVE", ChangeDirection.NEGATIVE.getText());
}
public void testSwitchStatement() {
switch (0) {
case 0:
// Once caused an ICE.
int test;
break;
}
}
/**
* Tests cases where the compiler will convert to a simple if or block.
*/
public void testSwitchStatementConversions() {
int i = 1;
switch (i) {
case 1:
i = 2;
}
assertEquals(2, i);
switch (i) {
case 1:
i = 3;
}
assertEquals(2, i);
switch (i) {
default:
i = 3;
}
assertEquals(3, i);
}
public void testSwitchStatementEmpty() {
switch (0) {
}
}
public void testSwitchStatementFallthroughs() {
int i = 1;
switch (i) {
case 1:
i = 2;
// fallthrough
case 2:
break;
case 3:
fail("Shouldn't get here");
}
assertEquals(2, i);
switch (i) {
case 1:
break;
case 2:
i = 3;
break;
case 3:
break;
case 4:
fail("Shouldn't get here");
break;
}
assertEquals(3, i);
}
public void testSwitchStatementWithUsefulDefault() {
switch (1) {
case 1:
case 2:
case 3: {
break;
}
case 4:
case 5:
case 6:
default:
fail("Shouldn't get here");
break;
}
}
public void testSwitchStatementWithUselessCases() {
switch (1) {
case 1:
case 2:
case 3: {
break;
}
case 4:
case 5:
case 6:
default:
break;
}
}
public void testSwitchStatementWontRemoveExpression() {
class Foo {
boolean setFlag;
int setFlag() {
setFlag = true;
return 3;
}
}
Foo foo = new Foo();
switch (foo.setFlag()) {
case 3:
break;
}
// Make sure that compiler didn't optimize away the switch statement's
// expression
assertTrue(foo.setFlag);
}
public void testUnaryPlus() {
int x, y = -7;
x = +y;
assertEquals(-7, x);
}
public void testUninstantiableAccess() {
UninstantiableType u = null;
try {
accessUninstantiableField(u);
fail("Expected JavaScriptException");
} catch (JavaScriptException expected) {
}
try {
accessUninstantiableMethod(u);
fail("Expected JavaScriptException");
} catch (JavaScriptException expected) {
}
try {
volatileUninstantiableType = (UninstantiableType) (new Object());
fail("Expected ClassCastException");
} catch (ClassCastException expected) {
}
try {
volatileInt = u.intField++;
fail("Expected NullPointerException (1)");
} catch (Exception expected) {
}
try {
volatileInt = u.intField--;
fail("Expected NullPointerException (2)");
} catch (Exception expected) {
}
try {
volatileInt = ++u.intField;
fail("Expected NullPointerException (3)");
} catch (Exception expected) {
}
try {
volatileInt = --u.intField;
fail("Expected NullPointerException (4)");
} catch (Exception expected) {
}
try {
u.intField = 0;
/*
* Currently, null.nullField gets pruned rather than being allowed to
* execute its side effect of tripping an NPE.
*/
// fail("Expected NullPointerException (5)");
} catch (Exception expected) {
}
try {
volatileBoolean = u.intField == 0;
fail("Expected NullPointerException (6)");
} catch (Exception expected) {
}
try {
volatileBoolean = u.returnInt() == 0;
fail("Expected NullPointerException (7)");
} catch (Exception expected) {
}
}
private void incrementBoxedInteger() {
// the following will need a temporary variable created
boxedInteger++;
}
private boolean returnFalse() {
return false;
}
}
class A {
public abstract class AA {
}
}
class B extends A {
{
new AA() {
};
}
}
// This construct used to cause an ICE
class CheckSubclassStaticInnerAndClinitOrdering extends Outer.StaticInner {
private static class Foo {
}
private static final Foo FOO = new Foo();
public CheckSubclassStaticInnerAndClinitOrdering() {
this(FOO);
}
public CheckSubclassStaticInnerAndClinitOrdering(Foo foo) {
// This used to be null due to clinit ordering issues
Assert.assertNotNull(foo);
}
}
class Outer {
public static class StaticInner {
}
}