Write KotlinSyntheticClass to generated classes for functions
That is SAM wrappers, callable reference wrappers, local functions and anonymous functions
This commit is contained in:
@@ -665,7 +665,7 @@ public class AsmUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void writeKotlinSyntheticClassAnnotation(@NotNull ClassBuilder v, @NotNull KotlinSyntheticClass.Kind kind) {
|
public static void writeKotlinSyntheticClassAnnotation(@NotNull ClassBuilder v, @NotNull KotlinSyntheticClass.Kind kind) {
|
||||||
AnnotationVisitor av = v.newAnnotation(Type.getObjectType(KotlinSyntheticClass.INTERNAL_NAME).getDescriptor(), true);
|
AnnotationVisitor av = v.newAnnotation(Type.getObjectType(KotlinSyntheticClass.CLASS_NAME.getInternalName()).getDescriptor(), true);
|
||||||
av.visit(ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
|
av.visit(ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
|
||||||
av.visitEnum(KotlinSyntheticClass.KIND_FIELD_NAME.asString(),
|
av.visitEnum(KotlinSyntheticClass.KIND_FIELD_NAME.asString(),
|
||||||
Type.getObjectType(KotlinSyntheticClass.KIND_INTERNAL_NAME).getDescriptor(),
|
Type.getObjectType(KotlinSyntheticClass.KIND_INTERNAL_NAME).getDescriptor(),
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import static org.jetbrains.asm4.Opcodes.*;
|
|||||||
import static org.jetbrains.jet.codegen.AsmUtil.*;
|
import static org.jetbrains.jet.codegen.AsmUtil.*;
|
||||||
import static org.jetbrains.jet.codegen.CodegenUtil.isConst;
|
import static org.jetbrains.jet.codegen.CodegenUtil.isConst;
|
||||||
import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
|
import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
|
||||||
|
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
|
||||||
|
|
||||||
public class ClosureCodegen extends ParentCodegenAwareImpl {
|
public class ClosureCodegen extends ParentCodegenAwareImpl {
|
||||||
private final PsiElement fun;
|
private final PsiElement fun;
|
||||||
@@ -59,6 +60,7 @@ public class ClosureCodegen extends ParentCodegenAwareImpl {
|
|||||||
private final CalculatedClosure closure;
|
private final CalculatedClosure closure;
|
||||||
private final Type asmType;
|
private final Type asmType;
|
||||||
private final int visibilityFlag;
|
private final int visibilityFlag;
|
||||||
|
private final KotlinSyntheticClass.Kind syntheticClassKind;
|
||||||
|
|
||||||
private Method constructor;
|
private Method constructor;
|
||||||
|
|
||||||
@@ -69,6 +71,7 @@ public class ClosureCodegen extends ParentCodegenAwareImpl {
|
|||||||
@Nullable ClassDescriptor samInterface,
|
@Nullable ClassDescriptor samInterface,
|
||||||
@NotNull Type closureSuperClass,
|
@NotNull Type closureSuperClass,
|
||||||
@NotNull CodegenContext parentContext,
|
@NotNull CodegenContext parentContext,
|
||||||
|
@NotNull KotlinSyntheticClass.Kind syntheticClassKind,
|
||||||
@NotNull LocalLookup localLookup,
|
@NotNull LocalLookup localLookup,
|
||||||
@NotNull FunctionGenerationStrategy strategy,
|
@NotNull FunctionGenerationStrategy strategy,
|
||||||
@Nullable MemberCodegen parentCodegen
|
@Nullable MemberCodegen parentCodegen
|
||||||
@@ -80,6 +83,7 @@ public class ClosureCodegen extends ParentCodegenAwareImpl {
|
|||||||
this.samInterface = samInterface;
|
this.samInterface = samInterface;
|
||||||
this.superClass = closureSuperClass;
|
this.superClass = closureSuperClass;
|
||||||
this.context = parentContext.intoClosure(funDescriptor, localLookup, typeMapper);
|
this.context = parentContext.intoClosure(funDescriptor, localLookup, typeMapper);
|
||||||
|
this.syntheticClassKind = syntheticClassKind;
|
||||||
this.strategy = strategy;
|
this.strategy = strategy;
|
||||||
|
|
||||||
ClassDescriptor classDescriptor = anonymousClassForFunction(bindingContext, funDescriptor);
|
ClassDescriptor classDescriptor = anonymousClassForFunction(bindingContext, funDescriptor);
|
||||||
@@ -121,6 +125,8 @@ public class ClosureCodegen extends ParentCodegenAwareImpl {
|
|||||||
);
|
);
|
||||||
cv.visitSource(fun.getContainingFile().getName(), null);
|
cv.visitSource(fun.getContainingFile().getName(), null);
|
||||||
|
|
||||||
|
writeKotlinSyntheticClassAnnotation(cv, syntheticClassKind);
|
||||||
|
|
||||||
JvmMethodSignature jvmMethodSignature = typeMapper.mapSignature(funDescriptor).replaceName(interfaceFunction.getName().toString());
|
JvmMethodSignature jvmMethodSignature = typeMapper.mapSignature(funDescriptor).replaceName(interfaceFunction.getName().toString());
|
||||||
generateBridge(cv, typeMapper.mapSignature(interfaceFunction).getAsmMethod(), jvmMethodSignature.getAsmMethod());
|
generateBridge(cv, typeMapper.mapSignature(interfaceFunction).getAsmMethod(), jvmMethodSignature.getAsmMethod());
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
|
|||||||
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
|
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
|
||||||
import static org.jetbrains.jet.lang.resolve.BindingContextUtils.getNotNull;
|
import static org.jetbrains.jet.lang.resolve.BindingContextUtils.getNotNull;
|
||||||
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
|
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.scopes.receivers.ReceiverValue.NO_RECEIVER;
|
import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER;
|
||||||
|
|
||||||
public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implements LocalLookup, ParentCodegenAware {
|
public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implements LocalLookup, ParentCodegenAware {
|
||||||
@@ -1288,7 +1289,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
return StackValue.none();
|
return StackValue.none();
|
||||||
}
|
}
|
||||||
|
|
||||||
StackValue closure = genClosure(function, null);
|
StackValue closure = genClosure(function, null, KotlinSyntheticClass.Kind.LOCAL_FUNCTION);
|
||||||
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, function);
|
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, function);
|
||||||
int index = lookupLocalIndex(descriptor);
|
int index = lookupLocalIndex(descriptor);
|
||||||
closure.put(OBJECT_TYPE, v);
|
closure.put(OBJECT_TYPE, v);
|
||||||
@@ -1304,18 +1305,23 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
return gen(expression.getFunctionLiteral().getBodyExpression());
|
return gen(expression.getFunctionLiteral().getBodyExpression());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return genClosure(expression.getFunctionLiteral(), null);
|
return genClosure(expression.getFunctionLiteral(), null, KotlinSyntheticClass.Kind.ANONYMOUS_FUNCTION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private StackValue genClosure(JetDeclarationWithBody declaration, @Nullable ClassDescriptor samInterfaceClass) {
|
@NotNull
|
||||||
|
private StackValue genClosure(
|
||||||
|
JetDeclarationWithBody declaration,
|
||||||
|
@Nullable ClassDescriptor samInterfaceClass,
|
||||||
|
@NotNull KotlinSyntheticClass.Kind kind
|
||||||
|
) {
|
||||||
FunctionDescriptor descriptor = bindingContext.get(BindingContext.FUNCTION, declaration);
|
FunctionDescriptor descriptor = bindingContext.get(BindingContext.FUNCTION, declaration);
|
||||||
assert descriptor != null : "Function is not resolved to descriptor: " + declaration.getText();
|
assert descriptor != null : "Function is not resolved to descriptor: " + declaration.getText();
|
||||||
|
|
||||||
Type closureSuperClass = samInterfaceClass == null ? getFunctionImplType(descriptor) : OBJECT_TYPE;
|
Type closureSuperClass = samInterfaceClass == null ? getFunctionImplType(descriptor) : OBJECT_TYPE;
|
||||||
|
|
||||||
ClosureCodegen closureCodegen = new ClosureCodegen(state, declaration, descriptor, samInterfaceClass, closureSuperClass, context,
|
ClosureCodegen closureCodegen = new ClosureCodegen(state, declaration, descriptor, samInterfaceClass, closureSuperClass, context,
|
||||||
this, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration), parentCodegen);
|
kind, this, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration), parentCodegen);
|
||||||
|
|
||||||
closureCodegen.gen();
|
closureCodegen.gen();
|
||||||
|
|
||||||
@@ -1934,42 +1940,43 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
return genSamInterfaceValue(argumentExpression, samInterface, this);
|
return genSamInterfaceValue(argumentExpression, samInterface, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
private StackValue genSamInterfaceValue(
|
private StackValue genSamInterfaceValue(
|
||||||
@NotNull JetExpression expression,
|
@NotNull JetExpression expression,
|
||||||
@NotNull JavaClassDescriptor samInterface,
|
@NotNull JavaClassDescriptor samInterface,
|
||||||
@NotNull JetVisitor<StackValue, StackValue> visitor
|
@NotNull JetVisitor<StackValue, StackValue> visitor
|
||||||
) {
|
) {
|
||||||
if (expression instanceof JetFunctionLiteralExpression) {
|
if (expression instanceof JetFunctionLiteralExpression) {
|
||||||
return genClosure(((JetFunctionLiteralExpression) expression).getFunctionLiteral(), samInterface);
|
return genClosure(((JetFunctionLiteralExpression) expression).getFunctionLiteral(), samInterface,
|
||||||
|
KotlinSyntheticClass.Kind.SAM_LAMBDA);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
Type asmType = state.getSamWrapperClasses().getSamWrapperClass(samInterface, (JetFile) expression.getContainingFile());
|
|
||||||
|
|
||||||
v.anew(asmType);
|
Type asmType = state.getSamWrapperClasses().getSamWrapperClass(samInterface, (JetFile) expression.getContainingFile());
|
||||||
v.dup();
|
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
v.anew(asmType);
|
||||||
Type functionType = typeMapper.mapType(samInterface.getFunctionTypeForSamInterface());
|
v.dup();
|
||||||
expression.accept(visitor, StackValue.none()).put(functionType, v);
|
|
||||||
|
|
||||||
Label ifNonNull = new Label();
|
@SuppressWarnings("ConstantConditions")
|
||||||
Label afterAll = new Label();
|
Type functionType = typeMapper.mapType(samInterface.getFunctionTypeForSamInterface());
|
||||||
|
expression.accept(visitor, StackValue.none()).put(functionType, v);
|
||||||
|
|
||||||
v.dup();
|
Label ifNonNull = new Label();
|
||||||
v.ifnonnull(ifNonNull);
|
Label afterAll = new Label();
|
||||||
|
|
||||||
// if null: pop function value and wrapper objects, put null
|
v.dup();
|
||||||
v.pop();
|
v.ifnonnull(ifNonNull);
|
||||||
v.pop2();
|
|
||||||
v.aconst(null);
|
|
||||||
v.goTo(afterAll);
|
|
||||||
|
|
||||||
v.mark(ifNonNull);
|
// if null: pop function value and wrapper objects, put null
|
||||||
v.invokespecial(asmType.getInternalName(), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, functionType));
|
v.pop();
|
||||||
|
v.pop2();
|
||||||
|
v.aconst(null);
|
||||||
|
v.goTo(afterAll);
|
||||||
|
|
||||||
v.mark(afterAll);
|
v.mark(ifNonNull);
|
||||||
return StackValue.onStack(asmType);
|
v.invokespecial(asmType.getInternalName(), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, functionType));
|
||||||
}
|
|
||||||
|
v.mark(afterAll);
|
||||||
|
return StackValue.onStack(asmType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -2456,8 +2463,9 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
Type closureSuperClass = typeMapper.mapType(kFunctionImpl);
|
Type closureSuperClass = typeMapper.mapType(kFunctionImpl);
|
||||||
|
|
||||||
CallableReferenceGenerationStrategy strategy = new CallableReferenceGenerationStrategy(state, functionDescriptor, resolvedCall);
|
CallableReferenceGenerationStrategy strategy = new CallableReferenceGenerationStrategy(state, functionDescriptor, resolvedCall);
|
||||||
ClosureCodegen closureCodegen = new ClosureCodegen(state, expression, functionDescriptor, null, closureSuperClass, context, this,
|
ClosureCodegen closureCodegen = new ClosureCodegen(state, expression, functionDescriptor, null, closureSuperClass, context,
|
||||||
strategy, getParentCodegen());
|
KotlinSyntheticClass.Kind.CALLABLE_REFERENCE_WRAPPER,
|
||||||
|
this, strategy, getParentCodegen());
|
||||||
|
|
||||||
closureCodegen.gen();
|
closureCodegen.gen();
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ import org.jetbrains.jet.lang.types.JetType;
|
|||||||
|
|
||||||
import static org.jetbrains.asm4.Opcodes.*;
|
import static org.jetbrains.asm4.Opcodes.*;
|
||||||
import static org.jetbrains.jet.codegen.AsmUtil.NO_FLAG_PACKAGE_PRIVATE;
|
import static org.jetbrains.jet.codegen.AsmUtil.NO_FLAG_PACKAGE_PRIVATE;
|
||||||
|
import static org.jetbrains.jet.codegen.AsmUtil.writeKotlinSyntheticClassAnnotation;
|
||||||
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
|
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
|
||||||
|
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
|
||||||
|
|
||||||
public class SamWrapperCodegen extends ParentCodegenAwareImpl {
|
public class SamWrapperCodegen extends ParentCodegenAwareImpl {
|
||||||
private static final String FUNCTION_FIELD_NAME = "function";
|
private static final String FUNCTION_FIELD_NAME = "function";
|
||||||
@@ -75,6 +77,8 @@ public class SamWrapperCodegen extends ParentCodegenAwareImpl {
|
|||||||
);
|
);
|
||||||
cv.visitSource(file.getName(), null);
|
cv.visitSource(file.getName(), null);
|
||||||
|
|
||||||
|
writeKotlinSyntheticClassAnnotation(cv, KotlinSyntheticClass.Kind.SAM_WRAPPER);
|
||||||
|
|
||||||
// e.g. ASM type for Function2
|
// e.g. ASM type for Function2
|
||||||
Type functionAsmType = state.getTypeMapper().mapType(functionType);
|
Type functionAsmType = state.getTypeMapper().mapType(functionType);
|
||||||
|
|
||||||
|
|||||||
@@ -16,20 +16,22 @@
|
|||||||
|
|
||||||
package org.jetbrains.jet.codegen;
|
package org.jetbrains.jet.codegen;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.Collections2;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.jet.ConfigurationKind;
|
import org.jetbrains.jet.ConfigurationKind;
|
||||||
import org.jetbrains.jet.OutputFile;
|
import org.jetbrains.jet.OutputFile;
|
||||||
import org.jetbrains.jet.OutputFileCollection;
|
|
||||||
import org.jetbrains.jet.lang.resolve.java.AbiVersionUtil;
|
import org.jetbrains.jet.lang.resolve.java.AbiVersionUtil;
|
||||||
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
||||||
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
|
|
||||||
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
|
|
||||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.ABI_VERSION_FIELD_NAME;
|
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.ABI_VERSION_FIELD_NAME;
|
||||||
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
|
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
|
||||||
|
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass.Kind.*;
|
||||||
|
|
||||||
public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase {
|
public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase {
|
||||||
public static final FqName PACKAGE_NAME = new FqName("test");
|
public static final FqName PACKAGE_NAME = new FqName("test");
|
||||||
@@ -40,36 +42,69 @@ public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase {
|
|||||||
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY);
|
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAnnotationIsWrittenOnPackagePart() throws Exception {
|
public void testPackagePart() {
|
||||||
loadText("package " + PACKAGE_NAME + "\n\nfun foo() = 42\n");
|
doTest("fun foo() = 42",
|
||||||
String facadeFileName = JvmClassName.byFqNameWithoutInnerClasses(PackageClassUtils.getPackageClassFqName(PACKAGE_NAME)).getInternalName() + ".class";
|
"-",
|
||||||
|
PACKAGE_PART);
|
||||||
OutputFileCollection outputFiles = generateClassesInFile();
|
|
||||||
for (OutputFile outputFile : outputFiles.asList()) {
|
|
||||||
// The file which is not a facade is a package part
|
|
||||||
String filePath = outputFile.getRelativePath();
|
|
||||||
if (filePath.equals(facadeFileName)) continue;
|
|
||||||
|
|
||||||
String fqName = filePath.substring(0, filePath.length() - ".class".length()).replace('/', '.');
|
|
||||||
Class<?> aClass = generateClass(fqName);
|
|
||||||
|
|
||||||
assertAnnotatedWithKind(aClass, "PACKAGE_PART");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fail("No package part was found: " + outputFiles.asList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAnnotationIsWrittenOnTraitImpl() throws Exception {
|
public void testTraitImpl() {
|
||||||
loadText("package " + PACKAGE_NAME + "\n\ntrait A { fun foo() = 42 }\n");
|
doTest("trait A { fun foo() = 42 }",
|
||||||
|
JvmAbi.TRAIT_IMPL_SUFFIX,
|
||||||
Class<?> aClass = generateClass(PACKAGE_NAME + ".A" + JvmAbi.TRAIT_IMPL_SUFFIX);
|
TRAIT_IMPL);
|
||||||
assertNotNull("TImpl is not generated", aClass);
|
|
||||||
assertAnnotatedWithKind(aClass, "TRAIT_IMPL");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertAnnotatedWithKind(@NotNull Class<?> aClass, @NotNull String expectedKind) {
|
public void testSamWrapper() {
|
||||||
Class<? extends Annotation> annotationClass = loadAnnotationClassQuietly(KotlinSyntheticClass.FQ_NAME.asString());
|
doTest("val f = {}\nval foo = Thread(f)",
|
||||||
|
"$sam",
|
||||||
|
SAM_WRAPPER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSamLambda() {
|
||||||
|
doTest("val foo = Thread { }",
|
||||||
|
"$",
|
||||||
|
SAM_LAMBDA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCallableReferenceWrapper() {
|
||||||
|
doTest("val f = String::get",
|
||||||
|
"$",
|
||||||
|
CALLABLE_REFERENCE_WRAPPER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLocalFunction() {
|
||||||
|
doTest("fun foo() { fun bar() {} }",
|
||||||
|
"$",
|
||||||
|
LOCAL_FUNCTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAnonymousFunction() {
|
||||||
|
doTest("val f = {}",
|
||||||
|
"$",
|
||||||
|
ANONYMOUS_FUNCTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTest(@NotNull String code, @NotNull final String classNamePart, @NotNull KotlinSyntheticClass.Kind expectedKind) {
|
||||||
|
loadText("package " + PACKAGE_NAME + "\n\n" + code);
|
||||||
|
List<OutputFile> output = generateClassesInFile().asList();
|
||||||
|
Collection<OutputFile> files = Collections2.filter(output, new Predicate<OutputFile>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(OutputFile file) {
|
||||||
|
return file.getRelativePath().contains(classNamePart);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertFalse("No files with \"" + classNamePart + "\" in the name are found: " + output, files.isEmpty());
|
||||||
|
assertTrue("Exactly one file with \"" + classNamePart + "\" in the name should be found: " + files, files.size() == 1);
|
||||||
|
|
||||||
|
String path = files.iterator().next().getRelativePath();
|
||||||
|
String fqName = path.substring(0, path.length() - ".class".length()).replace('/', '.');
|
||||||
|
Class<?> aClass = generateClass(fqName);
|
||||||
|
assertAnnotatedWithKind(aClass, expectedKind);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAnnotatedWithKind(@NotNull Class<?> aClass, @NotNull KotlinSyntheticClass.Kind expectedKind) {
|
||||||
|
Class<? extends Annotation> annotationClass = loadAnnotationClassQuietly(
|
||||||
|
KotlinSyntheticClass.CLASS_NAME.getFqNameForClassNameWithoutDollars().asString());
|
||||||
assertTrue("No KotlinSyntheticClass annotation found", aClass.isAnnotationPresent(annotationClass));
|
assertTrue("No KotlinSyntheticClass annotation found", aClass.isAnnotationPresent(annotationClass));
|
||||||
|
|
||||||
Annotation annotation = aClass.getAnnotation(annotationClass);
|
Annotation annotation = aClass.getAnnotation(annotationClass);
|
||||||
@@ -80,6 +115,6 @@ public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase {
|
|||||||
|
|
||||||
Object actualKind = CodegenTestUtil.getAnnotationAttribute(annotation, KotlinSyntheticClass.KIND_FIELD_NAME.asString());
|
Object actualKind = CodegenTestUtil.getAnnotationAttribute(annotation, KotlinSyntheticClass.KIND_FIELD_NAME.asString());
|
||||||
assertNotNull(actualKind);
|
assertNotNull(actualKind);
|
||||||
assertEquals("KotlinSyntheticClass annotation has the wrong kind", expectedKind, actualKind.toString());
|
assertEquals("KotlinSyntheticClass annotation has the wrong kind", expectedKind.toString(), actualKind.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-3
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.jet.lang.resolve.java;
|
package org.jetbrains.jet.lang.resolve.java;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||||
|
|
||||||
@@ -37,8 +38,7 @@ public final class JvmAnnotationNames {
|
|||||||
public static final FqName JETBRAINS_READONLY_ANNOTATION = new FqName("org.jetbrains.annotations.ReadOnly");
|
public static final FqName JETBRAINS_READONLY_ANNOTATION = new FqName("org.jetbrains.annotations.ReadOnly");
|
||||||
|
|
||||||
public static class KotlinSyntheticClass {
|
public static class KotlinSyntheticClass {
|
||||||
public static final FqName FQ_NAME = new FqName("kotlin.jvm.internal.KotlinSyntheticClass");
|
public static final JvmClassName CLASS_NAME = JvmClassName.byInternalName("kotlin/jvm/internal/KotlinSyntheticClass");
|
||||||
public static final String INTERNAL_NAME = JvmClassName.byFqNameWithoutInnerClasses(FQ_NAME).getInternalName();
|
|
||||||
public static final String KIND_INTERNAL_NAME = "kotlin/jvm/internal/KotlinSyntheticClass$Kind";
|
public static final String KIND_INTERNAL_NAME = "kotlin/jvm/internal/KotlinSyntheticClass$Kind";
|
||||||
|
|
||||||
public static final Name KIND_FIELD_NAME = Name.identifier("kind");
|
public static final Name KIND_FIELD_NAME = Name.identifier("kind");
|
||||||
@@ -49,7 +49,23 @@ public final class JvmAnnotationNames {
|
|||||||
*/
|
*/
|
||||||
public enum Kind {
|
public enum Kind {
|
||||||
PACKAGE_PART,
|
PACKAGE_PART,
|
||||||
TRAIT_IMPL
|
TRAIT_IMPL,
|
||||||
|
SAM_WRAPPER,
|
||||||
|
SAM_LAMBDA,
|
||||||
|
CALLABLE_REFERENCE_WRAPPER,
|
||||||
|
LOCAL_FUNCTION,
|
||||||
|
ANONYMOUS_FUNCTION,
|
||||||
|
;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Kind valueOfOrNull(@NotNull String name) {
|
||||||
|
try {
|
||||||
|
return valueOf(name);
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private KotlinSyntheticClass() {
|
private KotlinSyntheticClass() {
|
||||||
|
|||||||
+20
-5
@@ -19,26 +19,36 @@ package org.jetbrains.jet.lang.resolve.kotlin.header;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
|
||||||
|
|
||||||
public class KotlinClassHeader {
|
public class KotlinClassHeader {
|
||||||
|
|
||||||
public enum Kind {
|
public enum Kind {
|
||||||
|
INCOMPATIBLE_ABI_VERSION,
|
||||||
CLASS,
|
CLASS,
|
||||||
PACKAGE_FACADE,
|
PACKAGE_FACADE,
|
||||||
PACKAGE_PART,
|
SYNTHETIC_CLASS,
|
||||||
TRAIT_IMPL,
|
|
||||||
INCOMPATIBLE_ABI_VERSION
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Kind kind;
|
private final Kind kind;
|
||||||
private final int version;
|
private final int version;
|
||||||
private final String[] data;
|
private final String[] data;
|
||||||
|
private final KotlinSyntheticClass.Kind syntheticClassKind;
|
||||||
|
|
||||||
public KotlinClassHeader(@NotNull Kind kind, int version, @Nullable String[] annotationData) {
|
public KotlinClassHeader(
|
||||||
|
@NotNull Kind kind,
|
||||||
|
int version,
|
||||||
|
@Nullable String[] annotationData,
|
||||||
|
@Nullable KotlinSyntheticClass.Kind syntheticClassKind
|
||||||
|
) {
|
||||||
assert (annotationData == null) == (kind != Kind.CLASS && kind != Kind.PACKAGE_FACADE)
|
assert (annotationData == null) == (kind != Kind.CLASS && kind != Kind.PACKAGE_FACADE)
|
||||||
: "Annotation data should be not null only for CLASS and PACKAGE_FACADE";
|
: "Annotation data should be not null only for CLASS and PACKAGE_FACADE (kind=" + kind + ")";
|
||||||
|
assert (syntheticClassKind == null) == (kind != Kind.SYNTHETIC_CLASS)
|
||||||
|
: "Synthetic class kind should be present for SYNTHETIC_CLASS (kind=" + kind + ")";
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.data = annotationData;
|
this.data = annotationData;
|
||||||
|
this.syntheticClassKind = syntheticClassKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -54,4 +64,9 @@ public class KotlinClassHeader {
|
|||||||
public String[] getAnnotationData() {
|
public String[] getAnnotationData() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public KotlinSyntheticClass.Kind getSyntheticClassKind() {
|
||||||
|
return syntheticClassKind;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-30
@@ -33,29 +33,25 @@ import static org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass.Annotat
|
|||||||
import static org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader.Kind.*;
|
import static org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader.Kind.*;
|
||||||
|
|
||||||
public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor {
|
public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor {
|
||||||
private static final JvmClassName KOTLIN_CLASS_ANNOTATION = JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_CLASS);
|
private static final Map<JvmClassName, KotlinClassHeader.Kind> HEADER_KINDS = new HashMap<JvmClassName, KotlinClassHeader.Kind>();
|
||||||
private static final JvmClassName KOTLIN_PACKAGE_ANNOTATION = JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_PACKAGE);
|
|
||||||
private static final JvmClassName KOTLIN_SYNTHETIC_CLASS_ANNOTATION = JvmClassName.byInternalName(KotlinSyntheticClass.INTERNAL_NAME);
|
|
||||||
private static final Set<JvmClassName> ALL_SUPPORTED_ANNOTATIONS = new HashSet<JvmClassName>();
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ALL_SUPPORTED_ANNOTATIONS.add(KOTLIN_CLASS_ANNOTATION);
|
HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_CLASS), CLASS);
|
||||||
ALL_SUPPORTED_ANNOTATIONS.add(KOTLIN_PACKAGE_ANNOTATION);
|
HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_PACKAGE), PACKAGE_FACADE);
|
||||||
ALL_SUPPORTED_ANNOTATIONS.add(KOTLIN_SYNTHETIC_CLASS_ANNOTATION);
|
HEADER_KINDS.put(KotlinSyntheticClass.CLASS_NAME, SYNTHETIC_CLASS);
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
List<FqName> incompatible = Arrays.asList(OLD_JET_CLASS_ANNOTATION, OLD_JET_PACKAGE_CLASS_ANNOTATION, OLD_KOTLIN_CLASS,
|
List<FqName> incompatible = Arrays.asList(OLD_JET_CLASS_ANNOTATION, OLD_JET_PACKAGE_CLASS_ANNOTATION, OLD_KOTLIN_CLASS,
|
||||||
OLD_KOTLIN_PACKAGE, OLD_KOTLIN_PACKAGE_FRAGMENT, OLD_KOTLIN_TRAIT_IMPL);
|
OLD_KOTLIN_PACKAGE, OLD_KOTLIN_PACKAGE_FRAGMENT, OLD_KOTLIN_TRAIT_IMPL);
|
||||||
for (FqName fqName : incompatible) {
|
for (FqName fqName : incompatible) {
|
||||||
ALL_SUPPORTED_ANNOTATIONS.add(JvmClassName.byFqNameWithoutInnerClasses(fqName));
|
HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(fqName), INCOMPATIBLE_ABI_VERSION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int version = AbiVersionUtil.INVALID_VERSION;
|
private int version = AbiVersionUtil.INVALID_VERSION;
|
||||||
@Nullable
|
|
||||||
private String[] annotationData = null;
|
private String[] annotationData = null;
|
||||||
@Nullable
|
|
||||||
private KotlinClassHeader.Kind headerKind = null;
|
private KotlinClassHeader.Kind headerKind = null;
|
||||||
|
private KotlinSyntheticClass.Kind syntheticClassKind = null;
|
||||||
|
|
||||||
private ReadKotlinClassHeaderAnnotationVisitor() {
|
private ReadKotlinClassHeaderAnnotationVisitor() {
|
||||||
}
|
}
|
||||||
@@ -74,7 +70,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!AbiVersionUtil.isAbiVersionCompatible(version)) {
|
if (!AbiVersionUtil.isAbiVersionCompatible(version)) {
|
||||||
return new KotlinClassHeader(INCOMPATIBLE_ABI_VERSION, version, null);
|
return new KotlinClassHeader(INCOMPATIBLE_ABI_VERSION, version, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((headerKind == CLASS || headerKind == PACKAGE_FACADE) && annotationData == null) {
|
if ((headerKind == CLASS || headerKind == PACKAGE_FACADE) && annotationData == null) {
|
||||||
@@ -82,29 +78,29 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
|||||||
// We tell the outside world that there's really no annotation at all
|
// We tell the outside world that there's really no annotation at all
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new KotlinClassHeader(headerKind, version, annotationData);
|
|
||||||
|
return new KotlinClassHeader(headerKind, version, annotationData, syntheticClassKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName annotation) {
|
public AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName annotation) {
|
||||||
if (!ALL_SUPPORTED_ANNOTATIONS.contains(annotation)) return null;
|
KotlinClassHeader.Kind newKind = HEADER_KINDS.get(annotation);
|
||||||
|
if (newKind == null) return null;
|
||||||
|
|
||||||
if (headerKind != null) {
|
if (headerKind != null) {
|
||||||
// Ignore all Kotlin annotations except the first found
|
// Ignore all Kotlin annotations except the first found
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (annotation.equals(KOTLIN_CLASS_ANNOTATION) || annotation.equals(KOTLIN_PACKAGE_ANNOTATION)) {
|
headerKind = newKind;
|
||||||
headerKind = annotation.equals(KOTLIN_CLASS_ANNOTATION) ? CLASS : PACKAGE_FACADE;
|
|
||||||
|
if (newKind == CLASS || newKind == PACKAGE_FACADE) {
|
||||||
return kotlinClassOrPackageVisitor(annotation);
|
return kotlinClassOrPackageVisitor(annotation);
|
||||||
}
|
}
|
||||||
else if (annotation.equals(KOTLIN_SYNTHETIC_CLASS_ANNOTATION)) {
|
else if (newKind == SYNTHETIC_CLASS) {
|
||||||
return syntheticClassAnnotationVisitor();
|
return syntheticClassAnnotationVisitor();
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
headerKind = INCOMPATIBLE_ABI_VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -181,24 +177,17 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
|||||||
return new AnnotationArgumentVisitor() {
|
return new AnnotationArgumentVisitor() {
|
||||||
@Override
|
@Override
|
||||||
public void visit(@Nullable Name name, @Nullable Object value) {
|
public void visit(@Nullable Name name, @Nullable Object value) {
|
||||||
visitIntValueForSupportedAnnotation(name, value, KOTLIN_SYNTHETIC_CLASS_ANNOTATION);
|
visitIntValueForSupportedAnnotation(name, value, KotlinSyntheticClass.CLASS_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
|
public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
|
||||||
if (enumClassName.getInternalName().equals(KotlinSyntheticClass.KIND_INTERNAL_NAME) &&
|
if (enumClassName.getInternalName().equals(KotlinSyntheticClass.KIND_INTERNAL_NAME) &&
|
||||||
name.equals(KotlinSyntheticClass.KIND_FIELD_NAME)) {
|
name.equals(KotlinSyntheticClass.KIND_FIELD_NAME)) {
|
||||||
String value = enumEntryName.asString();
|
|
||||||
// Don't call KotlinSyntheticClass.Kind.valueOf() here, because it will throw an exception if there's no such value,
|
// Don't call KotlinSyntheticClass.Kind.valueOf() here, because it will throw an exception if there's no such value,
|
||||||
// but we don't want to fail if we're loading the header with an _incompatible_ ABI version
|
// but we don't want to fail if we're loading the header with an _incompatible_ ABI version
|
||||||
if (value.equals(KotlinSyntheticClass.Kind.PACKAGE_PART.toString())) {
|
syntheticClassKind = KotlinSyntheticClass.Kind.valueOfOrNull(enumEntryName.asString());
|
||||||
headerKind = PACKAGE_PART;
|
if (syntheticClassKind != null) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (value.equals(KotlinSyntheticClass.Kind.TRAIT_IMPL.toString())) {
|
|
||||||
headerKind = TRAIT_IMPL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (isAbiVersionCompatible(version)) {
|
if (isAbiVersionCompatible(version)) {
|
||||||
throw new IllegalStateException("Unexpected enum entry for synthetic class annotation: " +
|
throw new IllegalStateException("Unexpected enum entry for synthetic class annotation: " +
|
||||||
@@ -209,7 +198,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public AnnotationArgumentVisitor visitArray(@NotNull Name name) {
|
public AnnotationArgumentVisitor visitArray(@NotNull Name name) {
|
||||||
return unexpectedArgument(name, KOTLIN_SYNTHETIC_CLASS_ANNOTATION);
|
return unexpectedArgument(name, KotlinSyntheticClass.CLASS_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ public @interface KotlinSyntheticClass {
|
|||||||
|
|
||||||
public static enum Kind {
|
public static enum Kind {
|
||||||
PACKAGE_PART,
|
PACKAGE_PART,
|
||||||
TRAIT_IMPL
|
TRAIT_IMPL,
|
||||||
|
SAM_WRAPPER,
|
||||||
|
SAM_LAMBDA,
|
||||||
|
CALLABLE_REFERENCE_WRAPPER,
|
||||||
|
LOCAL_FUNCTION,
|
||||||
|
ANONYMOUS_FUNCTION,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import org.jetbrains.jet.lang.resolve.kotlin.KotlinBinaryClassCache
|
|||||||
import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader
|
import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader
|
||||||
import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader.Kind
|
import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader.Kind
|
||||||
import com.intellij.psi.ClassFileViewProvider
|
import com.intellij.psi.ClassFileViewProvider
|
||||||
|
import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass
|
||||||
|
|
||||||
|
|
||||||
//TODO: this should be done via generic mechanism (special header kind)
|
//TODO: this should be done via generic mechanism (special header kind)
|
||||||
@@ -47,7 +48,7 @@ public fun isKotlinCompiledFile(file: VirtualFile): Boolean {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
val header = KotlinBinaryClassCache.getKotlinBinaryClass(file).getClassHeader()
|
val header = KotlinBinaryClassCache.getKotlinBinaryClass(file).getClassHeader()
|
||||||
return header != null && header.getKind() != KotlinClassHeader.Kind.TRAIT_IMPL
|
return header != null && header.getSyntheticClassKind() != KotlinSyntheticClass.Kind.TRAIT_IMPL
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun isKotlinCompiledFileWithIncompatibleAbiVersion(file: VirtualFile): Boolean {
|
public fun isKotlinCompiledFileWithIncompatibleAbiVersion(file: VirtualFile): Boolean {
|
||||||
@@ -69,5 +70,5 @@ public fun isKotlinInternalCompiledFile(file: VirtualFile): Boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
val header = KotlinBinaryClassCache.getKotlinBinaryClass(file).getClassHeader()
|
val header = KotlinBinaryClassCache.getKotlinBinaryClass(file).getClassHeader()
|
||||||
return header?.getKind() == KotlinClassHeader.Kind.PACKAGE_PART
|
return header?.getKind() == KotlinClassHeader.Kind.SYNTHETIC_CLASS
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user