Files
kotlin-fork/compiler/tests/org/jetbrains/jet/codegen/PropertyGenTest.java
T
Alexander Udalov c853c9be03 Create empty method for annotated properties w/o fields
There was no place in bytecode where annotations on properties without backing
fields could be stored to. Now they're written on a synthetic empty void method
with a special name ("propertyName$annotations").

(Annotations on properties cannot simply be written on getters in bytecode,
since a getter can have annotations of its own.)
2013-08-23 17:39:21 +04:00

305 lines
12 KiB
Java

/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* 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 org.jetbrains.jet.codegen;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.jet.ConfigurationKind;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import static org.jetbrains.jet.codegen.CodegenTestUtil.assertIsCurrentTime;
import static org.jetbrains.jet.codegen.CodegenTestUtil.findDeclaredMethodByName;
public class PropertyGenTest extends CodegenTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY);
}
@NotNull
@Override
protected String getPrefix() {
return "properties";
}
public void testPrivateVal() throws Exception {
loadFile();
Class aClass = generateClass("PrivateVal");
Field[] fields = aClass.getDeclaredFields();
assertEquals(1, fields.length); // prop
Field field = fields[0];
assertEquals("prop", field.getName());
}
public void testPrivateVar() throws Exception {
loadFile();
Class aClass = generateClass("PrivateVar");
Object instance = aClass.newInstance();
Method setter = findDeclaredMethodByName(aClass, "setValueOfX");
setter.invoke(instance, 239);
Method getter = findDeclaredMethodByName(aClass, "getValueOfX");
assertEquals(239, ((Integer) getter.invoke(instance)).intValue());
}
public void testPublicVar() throws Exception {
loadText("class PublicVar() { public var foo : Int = 0; }");
Class aClass = generateClass("PublicVar");
Object instance = aClass.newInstance();
Method setter = findDeclaredMethodByName(aClass, "setFoo");
setter.invoke(instance, 239);
Method getter = findDeclaredMethodByName(aClass, "getFoo");
assertEquals(239, ((Integer) getter.invoke(instance)).intValue());
}
public void testAccessorsInInterface() {
loadText("class AccessorsInInterface() { public var foo : Int = 0; }");
Class aClass = generateClass("AccessorsInInterface");
assertNotNull(findDeclaredMethodByName(aClass, "getFoo"));
assertNotNull(findDeclaredMethodByName(aClass, "setFoo"));
}
public void testPrivatePropertyInNamespace() throws Exception {
loadText("private val x = 239");
Class nsClass = generateNamespaceSrcClass();
Field[] fields = nsClass.getDeclaredFields();
assertEquals(1, fields.length);
Field field = fields[0];
field.setAccessible(true);
assertEquals("x", field.getName());
assertEquals(Modifier.STATIC | Modifier.FINAL, field.getModifiers());
assertEquals(239, field.get(null));
}
public void testFieldPropertyAccess() throws Exception {
loadFile("properties/fieldPropertyAccess.kt");
Method method = generateFunction("increment");
assertEquals(1, method.invoke(null));
assertEquals(2, method.invoke(null));
}
public void testFieldGetter() throws Exception {
loadText("val now: Long get() = System.currentTimeMillis(); fun foo() = now");
Method method = generateFunction("foo");
assertIsCurrentTime((Long) method.invoke(null));
}
public void testFieldSetter() throws Exception {
loadFile();
Method method = generateFunction("append");
method.invoke(null, "IntelliJ ");
String value = (String) method.invoke(null, "IDEA");
if (!value.equals("IntelliJ IDEA")) {
System.out.println(generateToText());
throw new AssertionError(value);
}
assertEquals("IntelliJ IDEA", value);
}
public void testFieldSetterPlusEq() throws Exception {
loadFile();
Method method = generateFunction("append");
method.invoke(null, "IntelliJ ");
String value = (String) method.invoke(null, "IDEA");
assertEquals("IntelliJ IDEA", value);
}
public void testAccessorsWithoutBody() throws Exception {
loadText("class AccessorsWithoutBody() { protected var foo: Int = 349\n get\n private set\n fun setter() { foo = 610; } } ");
Class aClass = generateClass("AccessorsWithoutBody");
Object instance = aClass.newInstance();
Method getFoo = findDeclaredMethodByName(aClass, "getFoo");
getFoo.setAccessible(true);
assertTrue((getFoo.getModifiers() & Modifier.PROTECTED) != 0);
assertEquals(349, getFoo.invoke(instance));
Method setFoo = findDeclaredMethodByName(aClass, "setFoo");
setFoo.setAccessible(true);
assertTrue((setFoo.getModifiers() & Modifier.PRIVATE) != 0);
Method setter = findDeclaredMethodByName(aClass, "setter");
setter.invoke(instance);
assertEquals(610, getFoo.invoke(instance));
}
public void testInitializersForNamespaceProperties() throws Exception {
loadText("val x = System.currentTimeMillis()");
Method method = generateFunction("getX");
method.setAccessible(true);
assertIsCurrentTime((Long) method.invoke(null));
}
public void testPropertyReceiverOnStack() throws Exception {
loadFile();
Class aClass = generateClass("Evaluator");
Constructor constructor = aClass.getConstructor(StringBuilder.class);
StringBuilder sb = new StringBuilder("xyzzy");
Object instance = constructor.newInstance(sb);
Method method = aClass.getMethod("evaluateArg");
Integer result = (Integer) method.invoke(instance);
assertEquals(5, result.intValue());
}
public void testAbstractVal() throws Exception {
loadText("abstract class Foo { public abstract val x: String }");
Class aClass = generateClass("Foo");
assertNotNull(aClass.getMethod("getX"));
}
public void testVolatileProperty() throws Exception {
loadText("abstract class Foo { public volatile var x: String = \"\"; }");
Class aClass = generateClass("Foo");
Field x = aClass.getDeclaredField("x");
assertTrue((x.getModifiers() & Modifier.VOLATILE) != 0);
}
public void testKt160() throws Exception {
loadText("internal val s = java.lang.Double.toString(1.0)");
Method method = generateFunction("getS");
method.setAccessible(true);
assertEquals(method.invoke(null), "1.0");
}
public void testKt1846() {
loadFile("regressions/kt1846.kt");
Class aClass = generateClass("A");
try {
Method v1 = aClass.getMethod("getV1");
System.out.println(generateToText());
fail();
}
catch (NoSuchMethodException e) {
try {
Method v1 = aClass.getMethod("setV1");
System.out.println(generateToText());
fail();
}
catch (NoSuchMethodException ee) {
//
}
}
}
public void testKt2589() {
loadFile("regressions/kt2589.kt");
Class aClass = generateClass("Foo");
assertTrue((aClass.getModifiers() & Opcodes.ACC_FINAL) == 0);
try {
Field foo = aClass.getDeclaredField("foo");
assertTrue((foo.getModifiers() & Opcodes.ACC_PRIVATE) != 0);
assertTrue((foo.getModifiers() & Opcodes.ACC_FINAL) == 0);
Field bar = aClass.getDeclaredField("bar");
assertTrue((bar.getModifiers() & Opcodes.ACC_PRIVATE) != 0);
assertTrue((bar.getModifiers() & Opcodes.ACC_FINAL) != 0);
Method getFoo = aClass.getDeclaredMethod("getFoo");
assertTrue((getFoo.getModifiers() & Opcodes.ACC_PUBLIC) != 0);
assertTrue((getFoo.getModifiers() & Opcodes.ACC_FINAL) != 0);
Method getBar = aClass.getDeclaredMethod("getBar");
assertTrue((getBar.getModifiers() & Opcodes.ACC_PROTECTED) != 0);
assertTrue((getBar.getModifiers() & Opcodes.ACC_FINAL) == 0);
}
catch (Throwable e) {
System.out.println(generateToText());
throw new RuntimeException(e);
}
}
public void testKt2677() {
loadFile("regressions/kt2677.kt");
Class aClass = generateClass("DerivedWeatherReport");
Class bClass = aClass.getSuperclass();
try {
{
Method get = aClass.getDeclaredMethod("getForecast");
Type type = get.getGenericReturnType();
assertInstanceOf(type, ParameterizedType.class);
ParameterizedType parameterizedType = (ParameterizedType) type;
assertEquals(String.class, parameterizedType.getActualTypeArguments()[0]);
Method set = aClass.getDeclaredMethod("setForecast", (Class)parameterizedType.getRawType());
type = set.getGenericParameterTypes()[0];
parameterizedType = (ParameterizedType) type;
assertInstanceOf(type, ParameterizedType.class);
assertEquals(String.class, parameterizedType.getActualTypeArguments()[0]);
}
{
Method get = bClass.getDeclaredMethod("getForecast");
Type type = get.getGenericReturnType();
assertInstanceOf(type, ParameterizedType.class);
ParameterizedType parameterizedType = (ParameterizedType) type;
assertEquals(String.class, parameterizedType.getActualTypeArguments()[0]);
Method set = bClass.getDeclaredMethod("setForecast", (Class)parameterizedType.getRawType());
type = set.getGenericParameterTypes()[0];
parameterizedType = (ParameterizedType) type;
assertInstanceOf(type, ParameterizedType.class);
assertEquals(String.class, parameterizedType.getActualTypeArguments()[0]);
}
}
catch (Throwable e) {
System.out.println(generateToText());
throw new RuntimeException(e);
}
}
private static final String TEST_SYNTHETIC_METHOD_NAME = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(Name.identifier("property"));
public void testAnnotatedClassPropertyNoField() {
loadFile("properties/annotatedClassPropertyNoField.kt");
assertClassHasAnnotatedSyntheticMethod(generateClass("A"));
}
public void testAnnotatedPackagePropertyNoField() {
loadFile("properties/annotatedPackagePropertyNoField.kt");
String packageClassName = PackageClassUtils.getPackageClassName(FqName.ROOT);
for (String fileName : generateClassesInFile().files()) {
if (fileName.startsWith(packageClassName) && !fileName.equals(packageClassName + ".class")) {
// This should be package$src class
Class<?> a = generateClass(fileName.substring(0, fileName.length() - ".class".length()));
assertClassHasAnnotatedSyntheticMethod(a);
}
}
}
private static void assertClassHasAnnotatedSyntheticMethod(@NotNull Class<?> a) {
for (Method method : a.getDeclaredMethods()) {
if (TEST_SYNTHETIC_METHOD_NAME.equals(method.getName())) {
assertTrue(method.isSynthetic());
int modifiers = method.getModifiers();
assertTrue(Modifier.isFinal(modifiers));
assertTrue(Modifier.isStatic(modifiers));
assertTrue(Modifier.isPrivate(modifiers));
Annotation[] annotations = method.getDeclaredAnnotations();
assertSize(1, annotations);
assertEquals("@SomeAnnotation(value=OK)", annotations[0].toString());
return;
}
}
fail("Synthetic method for annotated property not found: " + TEST_SYNTHETIC_METHOD_NAME);
}
}