Generate "$kotlinClass"/"$kotlinPackage" reflection fields to every class

Some seemingly irrelevant tests were changed because now there's <clinit> in
almost every class and class initialization begins with executing it
This commit is contained in:
Alexander Udalov
2014-05-20 20:20:53 +04:00
parent 4ef089d2ed
commit 59777e7df6
17 changed files with 99 additions and 41 deletions
@@ -72,8 +72,7 @@ import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver;
import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.JAVA_STRING_TYPE;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.DelegationToTraitImpl;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin;
@@ -429,7 +428,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
@Override
protected void generateSyntheticParts() {
generateDelegatedPropertyMetadataArray();
generateStaticSyntheticFields();
generateFieldForSingleton();
@@ -463,10 +462,20 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
generateToArray();
genClosureFields(context.closure, v, state.getTypeMapper());
genClosureFields(context.closure, v, typeMapper);
}
private void generateDelegatedPropertyMetadataArray() {
private void generateStaticSyntheticFields() {
if (isAnnotationClass(descriptor)) {
// There's a bug in JDK 6 and 7 that prevents us from generating a static field in an annotation class:
// http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6857918
// TODO: make reflection work on annotation classes somehow
return;
}
generateReflectionObjectField(state, classAsmType, v, K_CLASS_IMPL_TYPE, JvmAbi.KOTLIN_CLASS_FIELD_NAME,
createOrGetClInitCodegen().v);
generatePropertyMetadataArrayFieldIfNeeded(classAsmType);
}
@@ -50,12 +50,12 @@ import java.util.List;
import static org.jetbrains.jet.codegen.AsmUtil.boxType;
import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.TraitImpl;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED;
import static org.jetbrains.jet.lang.resolve.BindingContext.VARIABLE;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.TraitImpl;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclarationContainer*/> extends ParentCodegenAware {
@@ -316,6 +316,27 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
return false;
}
public static void generateReflectionObjectField(
@NotNull GenerationState state,
@NotNull Type thisAsmType,
@NotNull ClassBuilder classBuilder,
@NotNull Type kImplType,
@NotNull String fieldName,
@NotNull InstructionAdapter v
) {
// TODO: generic signature
classBuilder.newField(NO_ORIGIN, ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, fieldName, kImplType.getDescriptor(),
null, null);
if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES) return;
v.anew(kImplType);
v.dup();
v.aconst(thisAsmType);
v.invokespecial(kImplType.getInternalName(), "<init>", "(Ljava/lang/Class;)V", false);
v.putstatic(thisAsmType.getInternalName(), fieldName, kImplType.getDescriptor());
}
protected void generatePropertyMetadataArrayFieldIfNeeded(@NotNull Type thisAsmType) {
List<JetProperty> delegatedProperties = new ArrayList<JetProperty>();
for (JetDeclaration declaration : ((JetDeclarationContainer) element).getDeclarations()) {
@@ -56,27 +56,32 @@ import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import java.util.*;
import static org.jetbrains.jet.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses;
import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.K_PACKAGE_IMPL_TYPE;
import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.*;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class PackageCodegen {
private final GenerationState state;
private final ClassBuilderOnDemand v;
private final GenerationState state;
private final Collection<JetFile> files;
private final Type packageClassType;
private final PackageFragmentDescriptor packageFragment;
private final PackageFragmentDescriptor compiledPackageFragment;
private final List<DeserializedCallableMemberDescriptor> previouslyCompiledCallables;
public PackageCodegen(@NotNull GenerationState state, @NotNull Collection<JetFile> files, @NotNull final FqName fqName) {
public PackageCodegen(@NotNull GenerationState state, @NotNull Collection<JetFile> files, @NotNull FqName fqName) {
this.state = state;
this.files = files;
this.packageFragment = getOnlyPackageFragment(fqName);
this.packageClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(getPackageClassFqName(fqName));
this.compiledPackageFragment = getCompiledPackageFragment(fqName);
this.previouslyCompiledCallables = filterDeserializedCallables(compiledPackageFragment);
@@ -88,14 +93,13 @@ public class PackageCodegen {
Collection<JetFile> files = PackageCodegen.this.files;
JetFile sourceFile = getRepresentativePackageFile(files);
String className = AsmUtil.internalNameByFqNameWithoutInnerClasses(getPackageClassFqName(fqName));
ClassBuilder v = PackageCodegen.this.state.getFactory()
.newVisitor(
PackageFacade(packageFragment == null ? compiledPackageFragment : packageFragment),
Type.getObjectType(className), PackagePartClassUtils.getPackageFilesWithCallables(files));
ClassBuilder v = PackageCodegen.this.state.getFactory().newVisitor(
PackageFacade(packageFragment == null ? compiledPackageFragment : packageFragment),
packageClassType, PackagePartClassUtils.getPackageFilesWithCallables(files)
);
v.defineClass(sourceFile, V1_6,
ACC_PUBLIC | ACC_FINAL,
className,
packageClassType.getInternalName(),
null,
"java/lang/Object",
ArrayUtil.EMPTY_STRING_ARRAY
@@ -216,18 +220,35 @@ public class PackageCodegen {
}
}
generateDelegationsToPreviouslyCompiled(generateCallableMemberTasks);
if (!generateCallableMemberTasks.isEmpty()) {
generatePackageFacadeClass(generateCallableMemberTasks, bindings);
}
}
if (generateCallableMemberTasks.isEmpty()) return;
private void generatePackageFacadeClass(
@NotNull Map<CallableMemberDescriptor, Runnable> tasks,
@NotNull List<JvmSerializationBindings> bindings
) {
generateKotlinPackageReflectionField();
for (CallableMemberDescriptor member : Ordering.from(MemberComparator.INSTANCE).sortedCopy(generateCallableMemberTasks.keySet())) {
generateCallableMemberTasks.get(member).run();
generateDelegationsToPreviouslyCompiled(tasks);
for (CallableMemberDescriptor member : Ordering.from(MemberComparator.INSTANCE).sortedCopy(tasks.keySet())) {
tasks.get(member).run();
}
bindings.add(v.getSerializationBindings());
writeKotlinPackageAnnotationIfNeeded(JvmSerializationBindings.union(bindings));
}
private void generateKotlinPackageReflectionField() {
MethodVisitor mv = v.newMethod(NO_ORIGIN, ACC_STATIC, "<clinit>", "()V", null, null);
MemberCodegen.generateReflectionObjectField(state, packageClassType, v, K_PACKAGE_IMPL_TYPE, JvmAbi.KOTLIN_PACKAGE_FIELD_NAME,
new InstructionAdapter(mv));
mv.visitInsn(RETURN);
FunctionCodegen.endVisit(mv, "static initializer", null);
}
private void writeKotlinPackageAnnotationIfNeeded(@NotNull JvmSerializationBindings bindings) {
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) {
return;
@@ -266,6 +266,9 @@ public class InlineCodegenUtil {
}
public static boolean isCapturedFieldName(@NotNull String fieldName) {
return fieldName.startsWith(CAPTURED_FIELD_PREFIX) || THIS$0.equals(fieldName) || RECEIVER$0.equals(fieldName);
// TODO: improve this heuristic
return (fieldName.startsWith(CAPTURED_FIELD_PREFIX) && !fieldName.equals(JvmAbi.KOTLIN_CLASS_FIELD_NAME)) ||
THIS$0.equals(fieldName) ||
RECEIVER$0.equals(fieldName);
}
}
@@ -38,6 +38,9 @@ public class AsmTypeConstants {
public static final Type PROPERTY_METADATA_TYPE = Type.getObjectType(BUILT_INS_PACKAGE_FQ_NAME + "/PropertyMetadata");
public static final Type PROPERTY_METADATA_IMPL_TYPE = Type.getObjectType(BUILT_INS_PACKAGE_FQ_NAME + "/PropertyMetadataImpl");
public static final Type K_CLASS_IMPL_TYPE = Type.getObjectType("kotlin/reflect/jvm/internal/KClassImpl");
public static final Type K_PACKAGE_IMPL_TYPE = Type.getObjectType("kotlin/reflect/jvm/internal/KPackageImpl");
public static final Type OBJECT_REF_TYPE = Type.getObjectType("kotlin/jvm/internal/Ref$ObjectRef");
public static Type getType(@NotNull Class<?> javaClass) {
@@ -20,15 +20,14 @@ import org.junit.Test;
import java.io.File;
import static junit.framework.Assert.*;
import static org.junit.Assert.assertEquals;
public class CompilerSmokeTest extends KotlinIntegrationTestBase {
@Test
public void compileAndRunHelloApp() throws Exception {
String jar = tmpdir.getTmpDir().getAbsolutePath() + File.separator + "hello.jar";
assertEquals("compilation failed", 0, runCompiler("hello.compile", "-src", "hello.kt", "-jar", jar));
assertEquals("compilation failed", 0, runCompiler("hello.compile", "-includeRuntime", "hello.kt", "-jar", jar));
runJava("hello.run", "-cp", jar, "Hello.HelloPackage");
}
@@ -36,7 +35,7 @@ public class CompilerSmokeTest extends KotlinIntegrationTestBase {
public void compileAndRunHelloAppFQMain() throws Exception {
String jar = tmpdir.getTmpDir().getAbsolutePath() + File.separator + "hello.jar";
assertEquals("compilation failed", 0, runCompiler("hello.compile", "-src", "hello.kt", "-jar", jar));
assertEquals("compilation failed", 0, runCompiler("hello.compile", "-includeRuntime", "hello.kt", "-jar", jar));
runJava("hello.run", "-cp", jar, "Hello.HelloPackage");
}
@@ -44,7 +43,7 @@ public class CompilerSmokeTest extends KotlinIntegrationTestBase {
public void compileAndRunHelloAppVarargMain() throws Exception {
String jar = tmpdir.getTmpDir().getAbsolutePath() + File.separator + "hello.jar";
assertEquals("compilation failed", 0, runCompiler("hello.compile", "-src", "hello.kt", "-jar", jar));
assertEquals("compilation failed", 0, runCompiler("hello.compile", "-includeRuntime", "hello.kt", "-jar", jar));
runJava("hello.run", "-cp", jar, "Hello.HelloPackage");
}
@@ -18,4 +18,4 @@ class B {
}
// 0 INVOKEVIRTUAL
// 4 INVOKESPECIAL
// 2 INVOKESPECIAL [AB]\.
@@ -13,4 +13,4 @@ fun box(): String {
}
// 0 INVOKEVIRTUAL
// 3 INVOKESPECIAL
// 2 INVOKESPECIAL B\.foo
@@ -40,11 +40,7 @@ public class PropertyGenTest extends CodegenTestCase {
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());
generateClass("PrivateVal").getDeclaredField("prop");
}
public void testPrivateVar() throws Exception {
@@ -45,6 +45,8 @@ public final class JvmAbi {
public static final String CLASS_OBJECT_FIELD = "object$";
public static final FqName K_OBJECT = new FqName("kotlin.jvm.internal.KObject");
public static final String KOTLIN_CLASS_FIELD_NAME = "$kotlinClass";
public static final String KOTLIN_PACKAGE_FIELD_NAME = "$kotlinPackage";
public static boolean isClassObjectFqName(@NotNull FqName fqName) {
return fqName.lastSegmentIs(Name.identifier(CLASS_OBJECT_CLASS_NAME));
@@ -1,7 +1,7 @@
LineBreakpoint created at memberFunFromTopLevel.kt:11
LineBreakpoint created at memberFunFromTopLevel.kt:13
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! memberFunFromTopLevel.MemberFunFromTopLevelPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
memberFunFromTopLevel.kt:10
memberFunFromTopLevel.kt:12
memberFunFromTopLevel.kt:4
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
@@ -1,7 +1,7 @@
LineBreakpoint created at memberGetterFromTopLevel.kt:13
LineBreakpoint created at memberGetterFromTopLevel.kt:15
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! memberGetterFromTopLevel.MemberGetterFromTopLevelPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
memberGetterFromTopLevel.kt:12
memberGetterFromTopLevel.kt:14
memberGetterFromTopLevel.kt:5
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
@@ -7,6 +7,8 @@ class A {
}
fun main(args: Array<String>) {
A()
//Breakpoint!
A().bar()
}
@@ -9,6 +9,8 @@ class A {
}
fun main(args: Array<String>) {
A()
//Breakpoint!
A().bar
}
@@ -220,8 +220,8 @@ public class KotlinJpsBuildTest extends AbstractKotlinJpsBuildTestCase {
// Check that outputs are located properly
File facadeWithA = findFileInOutputDir(findModule("module1"), "test/TestPackage.class");
File facadeWithB = findFileInOutputDir(findModule("module2"), "test/TestPackage.class");
assertSameElements(getMethodsOfClass(facadeWithA), "a", "getA");
assertSameElements(getMethodsOfClass(facadeWithB), "b", "getB", "setB");
assertSameElements(getMethodsOfClass(facadeWithA), "<clinit>", "a", "getA");
assertSameElements(getMethodsOfClass(facadeWithB), "<clinit>", "b", "getB", "setB");
checkPackageDeletedFromOutputWhen(Operation.CHANGE, "module1", "module1/src/a.kt", "test.TestPackage");
checkPackageDeletedFromOutputWhen(Operation.CHANGE, "module2", "module2/src/b.kt", "test.TestPackage");
@@ -4,4 +4,4 @@ fun a() {
}
val a = ""
val a = ""
@@ -4,4 +4,4 @@ fun b() {
}
var b = ""
var b = b()