/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* 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.devtools.j2objc.translate;
import com.google.devtools.j2objc.GenerationTest;
import com.google.devtools.j2objc.ast.Statement;
import java.io.IOException;
import java.util.List;
/**
* Unit tests for {@link Autoboxer} class.
*
* @author Tom Ball
*/
public class AutoboxerTest extends GenerationTest {
public void testDoNotBoxIntInVarargMethod() throws IOException {
String source = "public class Test { Test(String s) {} "
+ "int one(String s, int i) { return two(new Test(s), i, 1, 2); }"
+ "int two(Test t, int i, Integer ... args) { return 0; } }";
String translation = translateSourceFile(source, "Test", "Test.m");
// i should not be boxed since its argument is explicitly declared,
// but 1 and 2 should be because they are passed as varargs.
assertTranslation(translation, "twoWithTest:create_Test_initWithNSString_(s) "
+ "withInt:i withJavaLangIntegerArray:"
+ "[IOSObjectArray arrayWithObjects:(id[]){ JavaLangInteger_valueOfWithInt_(1), "
+ "JavaLangInteger_valueOfWithInt_(2) } count:2 type:JavaLangInteger_class_()]];");
}
public void testUnboxReturn() throws IOException {
String source = "public class Test { Integer value; int intValue() { return value; }}";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation, "return [((JavaLangInteger *) nil_chk(value_)) intValue];");
}
public void testBooleanAssignment() throws IOException {
String source = "boolean b = true; Boolean foo = Boolean.FALSE; b = foo;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(2));
assertEquals("b = [((JavaLangBoolean *) nil_chk(foo)) booleanValue];", result);
source = "boolean b = true; Boolean foo = Boolean.FALSE; foo = b;";
stmts = translateStatements(source);
result = generateStatement(stmts.get(2));
assertEquals("foo = JavaLangBoolean_valueOfWithBoolean_(b);", result);
}
public void testByteAssignment() throws IOException {
String source = "byte b = 5; Byte foo = Byte.valueOf((byte) 3); b = foo;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(2));
assertEquals("b = [foo charValue];", result);
source = "byte b = 5; Byte foo = Byte.valueOf((byte) 3); foo = b;";
stmts = translateStatements(source);
result = generateStatement(stmts.get(2));
assertEquals("foo = JavaLangByte_valueOfWithByte_(b);", result);
}
public void testCharAssignment() throws IOException {
String source = "char c = 'a'; Character foo = Character.valueOf('b'); c = foo;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(2));
assertEquals("c = [foo charValue];", result);
source = "char c = 'a'; Character foo = Character.valueOf('b'); foo = c;";
stmts = translateStatements(source);
result = generateStatement(stmts.get(2));
assertEquals("foo = JavaLangCharacter_valueOfWithChar_(c);", result);
}
public void testShortAssignment() throws IOException {
String source = "short s = 5; Short foo = Short.valueOf((short) 3); s = foo;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(2));
assertEquals("s = [foo shortValue];", result);
source = "short s = 5; Short foo = Short.valueOf((short) 3); foo = s;";
stmts = translateStatements(source);
result = generateStatement(stmts.get(2));
assertEquals("foo = JavaLangShort_valueOfWithShort_(s);", result);
}
public void testIntAssignment() throws IOException {
String source = "int i = 5; Integer foo = Integer.valueOf(3); i = foo;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(2));
assertEquals("i = [foo intValue];", result);
source = "int i = 5; Integer foo = Integer.valueOf(3); foo = i;";
stmts = translateStatements(source);
result = generateStatement(stmts.get(2));
assertEquals("foo = JavaLangInteger_valueOfWithInt_(i);", result);
}
public void testLongAssignment() throws IOException {
String source = "long l = 5; Long foo = Long.valueOf(3L); l = foo;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(2));
assertEquals("l = [foo longLongValue];", result);
source = "long l = 5; Long foo = Long.valueOf(3L); foo = l;";
stmts = translateStatements(source);
result = generateStatement(stmts.get(2));
assertEquals("foo = JavaLangLong_valueOfWithLong_(l);", result);
}
public void testFloatAssignment() throws IOException {
String source = "float f = 5.0f; Float foo = Float.valueOf(3.0f); f = foo;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(2));
assertEquals("f = [foo floatValue];", result);
source = "float f = 5.0f; Float foo = Float.valueOf(3.0f); foo = f;";
stmts = translateStatements(source);
result = generateStatement(stmts.get(2));
assertEquals("foo = JavaLangFloat_valueOfWithFloat_(f);", result);
}
public void testDoubleAssignment() throws IOException {
String source = "double d = 5.0; Double foo = Double.valueOf(3.0); d = foo;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(2));
assertEquals("d = [foo doubleValue];", result);
source = "double d = 5.0; Double foo = Double.valueOf(3.0); foo = d;";
stmts = translateStatements(source);
result = generateStatement(stmts.get(2));
assertEquals("foo = JavaLangDouble_valueOfWithDouble_(d);", result);
}
public void testInfixLeftOperand() throws IOException {
String source = "Integer test = new Integer(5); int result = test + 3;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(1));
assertEquals("jint result = [test intValue] + 3;", result);
}
public void testInfixRightOperand() throws IOException {
String source = "Integer test = new Integer(5); int result = 3 + test;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(1));
assertEquals("jint result = 3 + [test intValue];", result);
}
public void testInfixBothOperands() throws IOException {
String source = "Integer foo = new Integer(5); Integer bar = new Integer(3);"
+ "int result = foo + bar;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(2));
assertEquals("jint result = [foo intValue] + [bar intValue];", result);
}
public void testInfixNeitherOperand() throws IOException {
// Make sure primitive expressions aren't modified.
String source = "int result = 3 + 5;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(0));
assertEquals("jint result = 3 + 5;", result);
}
public void testVariableDeclaration() throws IOException {
String source = "Integer test = 3;";
List<Statement> stmts = translateStatements(source);
String result = generateStatement(stmts.get(0));
assertEquals("JavaLangInteger *test = JavaLangInteger_valueOfWithInt_(3);", result);
}
public void testMethodArgs() throws IOException {
String source = "public class Test { void foo(Integer i) {} "
+ "void test() { int i = 3; foo(i); }}";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation,
"[self fooWithJavaLangInteger:JavaLangInteger_valueOfWithInt_(i)];");
}
public void testConditionalExpression() throws IOException {
String translation = translateSourceFile(
"public class Test { "
+ "void test() { Boolean b = true ? false : null; } }",
"Test", "Test.m");
assertTranslation(translation, "JavaLangBoolean_valueOfWithBoolean_(false)");
}
public void testReturnWithConditional() throws IOException {
String translation = translateSourceFile(
"public class Test { "
+ "boolean test() { Boolean b = null; return b != null ? b : false; } }",
"Test", "Test.m");
assertTranslation(translation, "b != nil ? [b booleanValue] : false");
}
public void testConditionalOnBoxedValue() throws IOException {
String translation = translateSourceFile(
"public class Test { int test(Boolean b) { return b ? 1 : 2; } }", "Test", "Test.m");
assertTranslation(translation,
"return [((JavaLangBoolean *) nil_chk(b)) booleanValue] ? 1 : 2;");
}
public void testArrayInitializerNotBoxed() throws IOException {
// Verify that an array with an initializer that has elements of the same
// type isn't boxed.
String translation = translateSourceFile(
"public class Test { public int values[] = new int[] { 1, 2, 3 }; }",
"Test", "Test.m");
assertTranslation(translation, "[IOSIntArray newArrayWithInts:(jint[]){ 1, 2, 3 } count:3]");
translation = translateSourceFile(
"public class Test { private Integer i = 1; private Integer j = 2; private Integer k = 3;"
+ " public Integer values[] = new Integer[] { i, j, k }; }",
"Test", "Test.m");
assertTranslation(translation,
"[IOSObjectArray newArrayWithObjects:(id[]){ self->i_, self->j_, self->k_ } count:3 "
+ "type:JavaLangInteger_class_()]");
}
public void testArrayInitializerBoxed() throws IOException {
// Verify that an Integer array with an initializer that has int elements
// is boxed.
String translation = translateSourceFile(
"public class Test { private Integer i = 1; "
+ " public void test() { Integer values[] = new Integer[] { 1, 2, i }; }}",
"Test", "Test.m");
assertTranslation(translation,
"[IOSObjectArray arrayWithObjects:(id[]){ JavaLangInteger_valueOfWithInt_(1), "
+ "JavaLangInteger_valueOfWithInt_(2), i_ } count:3 type:JavaLangInteger_class_()]");
}
public void testArrayInitializerUnboxed() throws IOException {
// Verify that an int array with an initializer that has Integer elements
// is unboxed.
String translation = translateSourceFile(
"public class Test { private Integer i = 1; private Integer j = 2;"
+ " public void test() { int values[] = new int[] { i, j, 3 }; }}",
"Test", "Test.m");
assertTranslation(translation,
"[IOSIntArray arrayWithInts:(jint[]){ [((JavaLangInteger *) nil_chk(i_)) intValue], "
+ "[((JavaLangInteger *) nil_chk(j_)) intValue], 3 } count:3]");
}
public void testFieldArrayInitializerBoxed() throws IOException {
// Verify that an Integer array with an initializer that has int elements
// is boxed.
String translation = translateSourceFile(
"public class Test { private Integer i = 1; "
+ " public Integer values[] = new Integer[] { 1, 2, i }; }",
"Test", "Test.m");
assertTranslation(translation,
"[IOSObjectArray newArrayWithObjects:(id[]){ JavaLangInteger_valueOfWithInt_(1), "
+ "JavaLangInteger_valueOfWithInt_(2), self->i_ } count:3 type:JavaLangInteger_class_()]");
}
public void testFieldArrayInitializerUnboxed() throws IOException {
// Verify that an int array with an initializer that has Integer elements
// is unboxed.
String translation = translateSourceFile(
"public class Test { private Integer i = 1; private Integer j = 2;"
+ " public int values[] = new int[] { i, j, 3 }; }",
"Test", "Test.m");
assertTranslation(translation,
"[IOSIntArray newArrayWithInts:(jint[]){ "
+ "[self->i_ intValue], [self->j_ intValue], 3 } count:3]");
}
public void testBoxedTypeLiteral() throws IOException {
String source = "public class Test { Class c = int.class; }";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation, "JreStrongAssign(&self->c_, [IOSClass intClass]);");
}
public void testBoxedLhsOperatorAssignment() throws IOException {
String source = "public class Test { Integer i = 1; void foo() { i *= 2; } }";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation, "JreBoxedTimesAssignStrongInt(&i_, 2);");
}
public void testBoxedEnumConstructorArgs() throws IOException {
String source = "public enum Test { INT(0), BOOLEAN(false); Test(Object o) {}}";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation,
"Test_initWithId_withNSString_withInt_("
+ "e, JavaLangInteger_valueOfWithInt_(0), @\"INT\", 0);");
assertTranslation(translation,
"Test_initWithId_withNSString_withInt_("
+ "e, JavaLangBoolean_valueOfWithBoolean_(false), @\"BOOLEAN\", 1);");
}
public void testBoxedBoolInIf() throws IOException {
String source = "public class Test { Boolean b = false; void foo() { if (b) foo(); } }";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation, "if ([((JavaLangBoolean *) nil_chk(b_)) booleanValue])");
}
public void testBoxedBoolInWhile() throws IOException {
String source = "public class Test { Boolean b = false; void foo() { while (b) foo(); } }";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation, "while ([((JavaLangBoolean *) nil_chk(b_)) booleanValue])");
}
public void testBoxedBoolInDoWhile() throws IOException {
String source = "public class Test { "
+ " Boolean b = false; void foo() { do { foo(); } while (b); } }";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation, "while ([((JavaLangBoolean *) nil_chk(b_)) booleanValue])");
}
public void testBoxedBoolNegatedInWhile() throws IOException {
String source = "public class Test { Boolean b = false; void foo() { while (!b) foo(); } }";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation, "while (![((JavaLangBoolean *) nil_chk(b_)) booleanValue])");
}
public void testAutoboxCast() throws IOException {
String source = "public class Test { double doubleValue; "
+ "public int hashCode() { return ((Double) doubleValue).hashCode(); } }";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation, "[JavaLangDouble_valueOfWithDouble_(doubleValue_) hash]");
}
public void testAutoboxArrayIndex() throws IOException {
String source =
"public class Test { "
+ " Integer index() { return 1; }"
+ " void test() {"
+ " int[] array = new int[3];"
+ " array[index()] = 2; }}";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation,
"*IOSIntArray_GetRef(array, [((JavaLangInteger *) nil_chk([self index])) intValue]) = 2;");
}
public void testPrefixExpression() throws IOException {
String source =
"public class Test { void test() { "
+ " Integer iMinutes = new Integer(1); "
+ " Double iSeconds = new Double(0);"
+ " iMinutes = -iMinutes; iSeconds = -iSeconds; }}";
String translation = translateSourceFile(source, "Test", "Test.m");
assertTranslation(translation,
"iMinutes = JavaLangInteger_valueOfWithInt_(-[iMinutes intValue]);");
assertTranslation(translation,
"iSeconds = JavaLangDouble_valueOfWithDouble_(-[iSeconds doubleValue]);");
}
public void testStringConcatenation() throws IOException {
String translation = translateSourceFile(
"class Test { void test() { Boolean b = Boolean.TRUE; Integer i = new Integer(3); "
+ "String s = b + \"foo\" + i; } }", "Test", "Test.m");
assertTranslation(translation, "NSString *s = JreStrcat(\"@$@\", b, @\"foo\", i)");
}
public void testExtendedOperandsAreUnboxed() throws IOException {
String translation = translateSourceFile(
"class Test { void test() { Integer i1 = new Integer(2); Integer i2 = new Integer(3); "
+ "int i3 = 1 + 2 + i1 + i2; } }", "Test", "Test.m");
assertTranslation(translation, "int i3 = 1 + 2 + [i1 intValue] + [i2 intValue]");
}
public void testUnboxOfSwitchStatementExpression() throws IOException {
String translation = translateSourceFile(
"class Test { void test() {"
+ " Integer i = 3;"
+ " switch (i) { case 1: case 2: case 3: } } }", "Test", "Test.m");
assertTranslation(translation, "switch ([i intValue]) {");
}
public void testInvokeSuperMethodAutoboxing() throws IOException {
String translation = translateSourceFile("class Base { "
+ "public void print(Object o) { System.out.println(o); }}"
+ "public class Test extends Base {"
+ "@Override public void print(Object o) { super.print(123.456f); }}", "Test", "Test.m");
assertTranslation(translation,
"[super printWithId:JavaLangFloat_valueOfWithFloat_(123.456f)];");
}
public void testAssignIntLiteralToNonIntBoxedType() throws Exception {
String translation = translateSourceFile(
"class Test { void test() { Byte b = 3; Short s; s = 4; } }", "Test", "Test.m");
assertTranslation(translation, "JavaLangByte *b = JavaLangByte_valueOfWithByte_(3);");
assertTranslation(translation, "s = JavaLangShort_valueOfWithShort_(4);");
}
public void testBoxedIncrementAndDecrement() throws Exception {
String translation = translateSourceFile(
"class Test { void test() { Integer i = 1; i++; Byte b = 2; b--; Character c = 'a'; ++c; "
+ "Double d = 3.0; --d; } }", "Test", "Test.m");
assertTranslation(translation, "PostIncrInt(&i);");
assertTranslation(translation, "PostDecrByte(&b);");
assertTranslation(translation, "PreIncrChar(&c);");
assertTranslation(translation, "PreDecrDouble(&d);");
}
// Verify that passing a new Double to a method that takes a double is unboxed.
public void testUnboxedDoubleParameter() throws Exception {
String translation = translateSourceFile(
"class Test { void takesDouble(double d) {} void test() { takesDouble(new Double(1.2)); }}",
"Test", "Test.m");
assertTranslation(translation,
"[self takesDoubleWithDouble:[create_JavaLangDouble_initWithDouble_(1.2) doubleValue]];");
}
public void testWildcardBoxType() throws IOException {
String translation = translateSourceFile(
"class Test { interface Entry<T> { T getValue(); } "
+ "void test(Entry<? extends Long> entry) { long l = entry.getValue(); } }",
"Test", "Test.m");
assertTranslation(translation,
"jlong l = [((JavaLangLong *) nil_chk([((id<Test_Entry>) nil_chk(entry_)) "
+ "getValue])) longLongValue];");
}
public void testAssignmentWithCase() throws IOException {
String translation = translateSourceFile(
"class Test { void test() { Integer i; i = (Integer) 12; } }", "Test", "Test.m");
assertTranslation(translation, "i = JavaLangInteger_valueOfWithInt_(12);");
}
public void testAssertMessage() throws IOException {
String translation = translateSourceFile(
"class Test { void test(int i) { assert i == 0 : i; }}", "Test", "Test.m");
assertTranslation(translation,
"JreAssert((i == 0), (JavaLangInteger_valueOfWithInt_(i)));");
}
public void testNonWrapperObjectTypeCastToPrimitive() throws IOException {
String translation = translateSourceFile(
"class Test { int test(Object o) { return (int) o; } "
+ "int test2(Integer i) { return (int) i; } }", "Test", "Test.m");
assertTranslation(translation,
"return [((JavaLangInteger *) nil_chk((JavaLangInteger *) "
+ "cast_chk(o, [JavaLangInteger class]))) intValue];");
// Make sure we don't unnecessarily add a cast check if the object type
// matches the primitive cast type.
assertTranslation(translation,
"return [((JavaLangInteger *) nil_chk(i)) intValue];");
}
public void testBoxedOperators() throws IOException {
String translation = translateSourceFile(
"class Test { Integer si; Long sl; Float sf; Double sd;"
+ " Integer[] ai; Long[] al; Float[] af; Double[] ad;"
+ " void test(Integer wi, Long wl, Float wf, Double wd) {"
+ " si++; wi++; ++sl; ++wl; sf--; wf--; --sd; --wd;"
+ " si += 5; wi += 5; sl &= 6l; wl &= 6l;"
+ " si <<= 2; wi <<= 2; sl >>>= 3; wl >>>= 3;"
+ " ai[0]++; --al[1]; af[2] += 9; ad[3] -= 8; } }", "Test", "Test.m");
assertTranslatedLines(translation,
"JreBoxedPostIncrStrongInt(&si_);",
"JreBoxedPostIncrInt(&wi);",
"JreBoxedPreIncrStrongLong(&sl_);",
"JreBoxedPreIncrLong(&wl);",
"JreBoxedPostDecrStrongFloat(&sf_);",
"JreBoxedPostDecrFloat(&wf);",
"JreBoxedPreDecrStrongDouble(&sd_);",
"JreBoxedPreDecrDouble(&wd);",
"JreBoxedPlusAssignStrongInt(&si_, 5);",
"JreBoxedPlusAssignInt(&wi, 5);",
"JreBoxedBitAndAssignStrongLong(&sl_, 6l);",
"JreBoxedBitAndAssignLong(&wl, 6l);",
"JreBoxedLShiftAssignStrongInt(&si_, 2);",
"JreBoxedLShiftAssignInt(&wi, 2);",
"JreBoxedURShiftAssignStrongLong(&sl_, 3);",
"JreBoxedURShiftAssignLong(&wl, 3);",
"JreBoxedPostIncrArrayInt(IOSObjectArray_GetRef(nil_chk(ai_), 0));",
"JreBoxedPreDecrArrayLong(IOSObjectArray_GetRef(nil_chk(al_), 1));",
"JreBoxedPlusAssignArrayFloat(IOSObjectArray_GetRef(nil_chk(af_), 2), 9);",
"JreBoxedMinusAssignArrayDouble(IOSObjectArray_GetRef(nil_chk(ad_), 3), 8);");
}
// https://github.com/google/j2objc/issues/648
public void testLongCastOfInteger() throws IOException {
String translation = translateSourceFile(
"class Test { "
+ "void test() { "
+ " Integer tmp_int = new Integer(100); "
+ " long tmp_long = (long)tmp_int; }}", "Test", "Test.m");
assertTranslation(translation, "jlong tmp_long = [tmp_int longLongValue];");
}
}