From 094fa2f92bf6ef9ee16cad0640e5b92eb350ee5f Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 12 Mar 2015 22:29:49 +0300 Subject: [PATCH] Report error on using reflection without kotlin-reflect.jar in classpath Should be a warning because strictly speaking, the codegen doesn't need it during the compilation. It's an error at the moment only to let all clients of Kotlin reflection know that they must include kotlin-reflect.jar in the classpath --- .../kotlin/codegen/ExpressionCodegen.java | 47 ++++++++++++------- .../kotlin/codegen/state/GenerationState.java | 8 ++++ .../diagnostics/DefaultErrorMessagesJvm.java | 2 + .../resolve/jvm/diagnostics/ErrorsJvm.java | 4 ++ .../cli/jvm/noReflectionInClasspath.args | 3 ++ .../cli/jvm/noReflectionInClasspath.kt | 4 ++ .../cli/jvm/noReflectionInClasspath.out | 3 ++ .../cli/KotlincExecutableTestGenerated.java | 6 +++ .../jetbrains/kotlin/load/java/JvmAbi.java | 3 ++ 9 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 compiler/testData/cli/jvm/noReflectionInClasspath.args create mode 100644 compiler/testData/cli/jvm/noReflectionInClasspath.kt create mode 100644 compiler/testData/cli/jvm/noReflectionInClasspath.out diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index f4108c2fabc..10829573d27 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -65,6 +65,7 @@ import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant; import org.jetbrains.kotlin.resolve.constants.evaluate.EvaluatePackage; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage; +import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; import org.jetbrains.kotlin.resolve.scopes.receivers.*; import org.jetbrains.kotlin.types.Approximation; @@ -97,6 +98,7 @@ import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*; import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.OtherOrigin; import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.TraitImpl; import static org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER; +import static org.jetbrains.kotlin.serialization.deserialization.DeserializationPackage.findClassAcrossModuleDependencies; import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_PRIVATE; public class ExpressionCodegen extends JetVisitor implements LocalLookup { @@ -2665,6 +2667,8 @@ public class ExpressionCodegen extends JetVisitor implem @Override public StackValue visitClassLiteralExpression(@NotNull JetClassLiteralExpression expression, StackValue data) { + checkReflectionIsAvailable(expression); + JetType type = bindingContext.get(EXPRESSION_TYPE, expression); assert type != null; @@ -2687,25 +2691,34 @@ public class ExpressionCodegen extends JetVisitor implem } VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression); - if (variableDescriptor != null) { - VariableDescriptor descriptor = (VariableDescriptor) resolvedCall.getResultingDescriptor(); - - DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); - if (containingDeclaration instanceof PackageFragmentDescriptor) { - return generateTopLevelPropertyReference(descriptor); - } - else if (containingDeclaration instanceof ClassDescriptor) { - return generateMemberPropertyReference(descriptor, (ClassDescriptor) containingDeclaration); - } - else if (containingDeclaration instanceof ScriptDescriptor) { - return generateMemberPropertyReference(descriptor, ((ScriptDescriptor) containingDeclaration).getClassDescriptor()); - } - else { - throw new UnsupportedOperationException("Unsupported callable reference container: " + containingDeclaration); - } + if (variableDescriptor == null) { + throw new UnsupportedOperationException("Unsupported callable reference expression: " + expression.getText()); } - throw new UnsupportedOperationException("Unsupported callable reference expression: " + expression.getText()); + // TODO: this diagnostic should also be reported on function references once they obtain reflection + checkReflectionIsAvailable(expression); + + VariableDescriptor descriptor = (VariableDescriptor) resolvedCall.getResultingDescriptor(); + + DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); + if (containingDeclaration instanceof PackageFragmentDescriptor) { + return generateTopLevelPropertyReference(descriptor); + } + else if (containingDeclaration instanceof ClassDescriptor) { + return generateMemberPropertyReference(descriptor, (ClassDescriptor) containingDeclaration); + } + else if (containingDeclaration instanceof ScriptDescriptor) { + return generateMemberPropertyReference(descriptor, ((ScriptDescriptor) containingDeclaration).getClassDescriptor()); + } + else { + throw new UnsupportedOperationException("Unsupported callable reference container: " + containingDeclaration); + } + } + + private void checkReflectionIsAvailable(@NotNull JetExpression expression) { + if (findClassAcrossModuleDependencies(state.getModule(), JvmAbi.REFLECTION_FACTORY_IMPL) == null) { + state.getDiagnostics().report(ErrorsJvm.NO_REFLECTION_IN_CLASS_PATH.on(expression, expression)); + } } @NotNull diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.java index ae0798af134..6702f836377 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.java @@ -124,6 +124,8 @@ public class GenerationState { @NotNull private final ModuleDescriptor module; + private final DiagnosticSink diagnostics; + @NotNull private final Collection packagesWithObsoleteParts; @@ -182,6 +184,7 @@ public class GenerationState { builderFactory = new OptimizationClassBuilderFactory(builderFactory); } + this.diagnostics = diagnostics; this.classFileFactory = new ClassFileFactory(this, new BuilderFactoryForDuplicateSignatureDiagnostics( builderFactory, this.bindingContext, diagnostics)); @@ -271,6 +274,11 @@ public class GenerationState { return runtimeTypes; } + @NotNull + public DiagnosticSink getDiagnostics() { + return diagnostics; + } + public boolean isInlineEnabled() { return !disableInline; } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index c3bc9f63b21..1ab3590b857 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -53,6 +53,8 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(ErrorsJvm.NATIVE_DECLARATION_IN_TRAIT, "Members of traits can not be native"); MAP.put(ErrorsJvm.NATIVE_DECLARATION_CANNOT_BE_INLINED, "Members of traits can not be inlined"); + MAP.put(ErrorsJvm.NO_REFLECTION_IN_CLASS_PATH, "Expression ''{0}'' uses reflection which is not found in compilation classpath. Make sure you have kotlin-reflect.jar in the classpath", Renderers.ELEMENT_TEXT); + MAP.put(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS, "Expected type does not accept nulls in {0}, but the value may be null in {1}", Renderers.TO_STRING, Renderers.TO_STRING); } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index 4f7a82b32a3..5e86539e8bd 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2; import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.psi.JetDeclaration; import org.jetbrains.kotlin.psi.JetElement; +import org.jetbrains.kotlin.psi.JetExpression; import static org.jetbrains.kotlin.diagnostics.PositioningStrategies.*; import static org.jetbrains.kotlin.diagnostics.Severity.ERROR; @@ -46,6 +47,9 @@ public interface ErrorsJvm { DiagnosticFactory0 NATIVE_DECLARATION_IN_TRAIT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0 NATIVE_DECLARATION_CANNOT_BE_INLINED = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); + // TODO: make this a warning + DiagnosticFactory1 NO_REFLECTION_IN_CLASS_PATH = DiagnosticFactory1.create(ERROR); + enum NullabilityInformationSource { KOTLIN { @NotNull diff --git a/compiler/testData/cli/jvm/noReflectionInClasspath.args b/compiler/testData/cli/jvm/noReflectionInClasspath.args new file mode 100644 index 00000000000..6c45450a152 --- /dev/null +++ b/compiler/testData/cli/jvm/noReflectionInClasspath.args @@ -0,0 +1,3 @@ +$TESTDATA_DIR$/noReflectionInClasspath.kt +-d +$TEMP_DIR$ diff --git a/compiler/testData/cli/jvm/noReflectionInClasspath.kt b/compiler/testData/cli/jvm/noReflectionInClasspath.kt new file mode 100644 index 00000000000..aea00a248ca --- /dev/null +++ b/compiler/testData/cli/jvm/noReflectionInClasspath.kt @@ -0,0 +1,4 @@ +class Foo(val prop: Any) + +fun t1() = Foo::prop +fun t2() = Foo::class diff --git a/compiler/testData/cli/jvm/noReflectionInClasspath.out b/compiler/testData/cli/jvm/noReflectionInClasspath.out new file mode 100644 index 00000000000..2569a8b3e51 --- /dev/null +++ b/compiler/testData/cli/jvm/noReflectionInClasspath.out @@ -0,0 +1,3 @@ +ERROR: compiler/testData/cli/jvm/noReflectionInClasspath.kt: (3, 12) Expression 'Foo::prop' uses reflection which is not found in compilation classpath. Make sure you have kotlin-reflect.jar in the classpath +ERROR: compiler/testData/cli/jvm/noReflectionInClasspath.kt: (4, 12) Expression 'Foo::class' uses reflection which is not found in compilation classpath. Make sure you have kotlin-reflect.jar in the classpath +COMPILATION_ERROR diff --git a/compiler/tests/org/jetbrains/kotlin/cli/KotlincExecutableTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cli/KotlincExecutableTestGenerated.java index 47d8bf4aebb..b05349c9cbf 100644 --- a/compiler/tests/org/jetbrains/kotlin/cli/KotlincExecutableTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cli/KotlincExecutableTestGenerated.java @@ -90,6 +90,12 @@ public class KotlincExecutableTestGenerated extends AbstractKotlincExecutableTes doJvmTest(fileName); } + @TestMetadata("noReflectionInClasspath.args") + public void testNoReflectionInClasspath() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/cli/jvm/noReflectionInClasspath.args"); + doJvmTest(fileName); + } + @TestMetadata("nonExistingClassPathAndAnnotationsPath.args") public void testNonExistingClassPathAndAnnotationsPath() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/cli/jvm/nonExistingClassPathAndAnnotationsPath.args"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAbi.java b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAbi.java index 08dc00d423c..b09ab289b9e 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAbi.java +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAbi.java @@ -17,6 +17,8 @@ package org.jetbrains.kotlin.load.java; import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.name.ClassId; +import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.Name; public final class JvmAbi { @@ -41,6 +43,7 @@ public final class JvmAbi { public static final String KOTLIN_CLASS_FIELD_NAME = "$kotlinClass"; public static final String KOTLIN_PACKAGE_FIELD_NAME = "$kotlinPackage"; + public static final ClassId REFLECTION_FACTORY_IMPL = ClassId.topLevel(new FqName("kotlin.reflect.jvm.internal.ReflectionFactoryImpl")); //TODO: To be removed after kotlin M11 @Deprecated