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:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user