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