From 199790276ba43ae71d4ddf374d9c54cfaa0a95fb Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 26 Dec 2016 18:15:20 +0300 Subject: [PATCH] JS: prohibit `dynamic` values as handlers of property delegates. Prohibit delegation of classes by dynamic values. See KT-15283 --- .../dynamicTypes/classDelegateBy.kt | 13 +++++ .../{delegationBy.txt => classDelegateBy.txt} | 21 ++++++-- .../dynamicTypes/conventions.dynamic.txt | 3 -- .../dynamicTypes/conventions.kt | 3 -- .../dynamicTypes/conventions.txt | 3 -- .../dynamicTypes/delegationBy.kt | 5 -- .../propertyDelegateBy.dynamic.txt | 4 ++ .../dynamicTypes/propertyDelegateBy.kt | 27 ++++++++++ .../dynamicTypes/propertyDelegateBy.txt | 38 +++++++++++++ .../DiagnosticsTestWithJsStdLibGenerated.java | 18 ++++--- .../js/resolve/JsPlatformConfigurator.kt | 1 + .../diagnostics/DefaultErrorMessagesJs.kt | 2 + .../js/resolve/diagnostics/ErrorsJs.java | 2 + .../JsDynamicDeclarationChecker.kt | 54 +++++++++++++++++++ 14 files changed, 169 insertions(+), 25 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/classDelegateBy.kt rename compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/{delegationBy.txt => classDelegateBy.txt} (51%) delete mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/delegationBy.kt create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.dynamic.txt create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.kt create mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.txt create mode 100644 js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsDynamicDeclarationChecker.kt diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/classDelegateBy.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/classDelegateBy.kt new file mode 100644 index 00000000000..20c9e145b0d --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/classDelegateBy.kt @@ -0,0 +1,13 @@ +val x: dynamic = 23 + +interface I { + fun foo(): String +} + +class C : I by x + +object O : I by x + +fun box(): String { + return object : I by x {}.foo() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/delegationBy.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/classDelegateBy.txt similarity index 51% rename from compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/delegationBy.txt rename to compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/classDelegateBy.txt index 67a85ee74e7..d70d03cb157 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/delegationBy.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/classDelegateBy.txt @@ -1,16 +1,27 @@ package -public final class C : Tr { - public constructor C(/*0*/ d: dynamic) +public val x: dynamic = 23 +public fun box(): kotlin.String + +public final class C : I { + public constructor C() public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*delegation*/ fun foo(): kotlin.Unit + public open override /*1*/ /*delegation*/ fun foo(): kotlin.String public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } -public interface Tr { +public interface I { public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public abstract fun foo(): kotlin.Unit + public abstract fun foo(): kotlin.String + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public object O : I { + private constructor O() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*delegation*/ fun foo(): kotlin.String public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.dynamic.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.dynamic.txt index 08660b43e2a..f37ded8b856 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.dynamic.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.dynamic.txt @@ -52,6 +52,3 @@ public final fun get(/*0*/ p0: dynamic): dynamic public final fun divAssign(/*0*/ p0: dynamic): dynamic public final fun get(/*0*/ p0: dynamic): dynamic public final fun remAssign(/*0*/ p0: dynamic): dynamic -public final fun getValue(/*0*/ p0: dynamic, /*1*/ p1: dynamic): dynamic -public final fun getValue(/*0*/ p0: dynamic, /*1*/ p1: dynamic): dynamic -public final fun setValue(/*0*/ p0: dynamic, /*1*/ p1: dynamic, /*2*/ p2: dynamic): dynamic diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.kt index 1995e5c534f..032b1f4d746 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.kt @@ -81,6 +81,3 @@ fun test(d: dynamic) { d[1] %= 1 } -val dyn: dynamic = null -val foo : Int by dyn -var bar : Int by dyn diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.txt index 1da44ec906c..dab53768f2f 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/conventions.txt @@ -1,6 +1,3 @@ package -public var bar: kotlin.Int -public val dyn: dynamic = null -public val foo: kotlin.Int public fun test(/*0*/ d: dynamic): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/delegationBy.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/delegationBy.kt deleted file mode 100644 index b21ee696104..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/delegationBy.kt +++ /dev/null @@ -1,5 +0,0 @@ -interface Tr { - fun foo() -} - -class C(d: dynamic) : Tr by d \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.dynamic.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.dynamic.txt new file mode 100644 index 00000000000..10c95283ca3 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.dynamic.txt @@ -0,0 +1,4 @@ +public final fun getValue(/*0*/ p0: dynamic, /*1*/ p1: dynamic): dynamic +public final fun setValue(/*0*/ p0: dynamic, /*1*/ p1: dynamic, /*2*/ p2: dynamic): dynamic +public final fun getValue(/*0*/ p0: dynamic, /*1*/ p1: dynamic): dynamic +public final fun getValue(/*0*/ p0: dynamic, /*1*/ p1: dynamic): dynamic diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.kt new file mode 100644 index 00000000000..e794dee22c4 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.kt @@ -0,0 +1,27 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE + +external val x: dynamic + +var y: Any? by x + +fun foo() { + val a: Any by x +} + +class C { + val a: dynamic by x +} + +class A { + operator fun provideDelegate(host: Any?, p: Any): dynamic = TODO("") +} + +val z: Any? by A() + +class DynamicHandler { + operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): dynamic = 23 +} + +class B { + val x: dynamic by DynamicHandler() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.txt new file mode 100644 index 00000000000..e2be378488a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.txt @@ -0,0 +1,38 @@ +package + +public external val x: dynamic +public var y: kotlin.Any? +public val z: kotlin.Any? +public fun foo(): kotlin.Unit + +public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final operator fun provideDelegate(/*0*/ host: kotlin.Any?, /*1*/ p: kotlin.Any): dynamic + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class B { + public constructor B() + public final val x: dynamic + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class C { + public constructor C() + public final val a: dynamic + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class DynamicHandler { + public constructor DynamicHandler() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ property: kotlin.reflect.KProperty<*>): dynamic + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java index 8a4a0cbf81a..92cfeb648a0 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java @@ -86,6 +86,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes doTest(fileName); } + @TestMetadata("classDelegateBy.kt") + public void testClassDelegateBy() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/classDelegateBy.kt"); + doTest(fileName); + } + @TestMetadata("comparisonToNull.kt") public void testComparisonToNull() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/comparisonToNull.kt"); @@ -104,12 +110,6 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes doTest(fileName); } - @TestMetadata("delegationBy.kt") - public void testDelegationBy() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/delegationBy.kt"); - doTest(fileName); - } - @TestMetadata("destructuring.kt") public void testDestructuring() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/destructuring.kt"); @@ -242,6 +242,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes doTest(fileName); } + @TestMetadata("propertyDelegateBy.kt") + public void testPropertyDelegateBy() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/propertyDelegateBy.kt"); + doTest(fileName); + } + @TestMetadata("protected.kt") public void testProtected() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/protected.kt"); diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt index b5ac05d2758..e18bc19e902 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt @@ -39,6 +39,7 @@ object JsPlatformConfigurator : PlatformConfigurator( JsNameChecker, JsModuleChecker, JsExternalChecker, JsInheritanceChecker, JsRuntimeAnnotationChecker, + JsDynamicDeclarationChecker, HeaderImplDeclarationChecker() ), additionalCallCheckers = listOf(ReifiedTypeParameterSubstitutionChecker(), JsModuleCallChecker, JsDynamicCallChecker), diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt index c82495c3d66..5c69f21aa78 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt @@ -71,6 +71,8 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { put(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC, "Wrong operation with dynamic value: {0}", Renderers.STRING) put(ErrorsJs.SPREAD_OPERATOR_IN_DYNAMIC_CALL, "Can't apply spread operator in dynamic call") + put(ErrorsJs.DELEGATION_BY_DYNAMIC, "Can't delegate to dynamic value") + put(ErrorsJs.PROPERTY_DELEGATION_BY_DYNAMIC, "Can't apply property delegation by dynamic handler") put(ErrorsJs.RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION, "Runtime annotation can't be put on external declaration") put(ErrorsJs.RUNTIME_ANNOTATION_NOT_SUPPORTED, "Reflection is not supported in JavaScript target, therefore you won't be able " + diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java index a9f11b71a42..0384baeaf70 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java @@ -71,6 +71,8 @@ public interface ErrorsJs { DiagnosticFactory1 WRONG_OPERATION_WITH_DYNAMIC = DiagnosticFactory1.create(ERROR); DiagnosticFactory0 SPREAD_OPERATOR_IN_DYNAMIC_CALL = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 DELEGATION_BY_DYNAMIC = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 PROPERTY_DELEGATION_BY_DYNAMIC = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION = DiagnosticFactory0.create( ERROR, DECLARATION_SIGNATURE_OR_DEFAULT); diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsDynamicDeclarationChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsDynamicDeclarationChecker.kt new file mode 100644 index 00000000000..2d0c1c90043 --- /dev/null +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsDynamicDeclarationChecker.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.js.resolve.diagnostics + +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors +import org.jetbrains.kotlin.diagnostics.DiagnosticSink +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.checkers.SimpleDeclarationChecker +import org.jetbrains.kotlin.types.isDynamic + +object JsDynamicDeclarationChecker : SimpleDeclarationChecker { + override fun check( + declaration: KtDeclaration, descriptor: DeclarationDescriptor, + diagnosticHolder: DiagnosticSink, bindingContext: BindingContext + ) { + if (declaration is KtProperty && descriptor is VariableDescriptorWithAccessors) { + declaration.delegateExpression?.let { delegateExpression -> + val provideDelegateCall = bindingContext[BindingContext.PROVIDE_DELEGATE_RESOLVED_CALL, descriptor] + if (provideDelegateCall != null && provideDelegateCall.resultingDescriptor.returnType?.isDynamic() == true || + bindingContext.getType(delegateExpression)?.isDynamic() == true + ) { + diagnosticHolder.report(ErrorsJs.PROPERTY_DELEGATION_BY_DYNAMIC.on(delegateExpression)) + } + } + } + else if (declaration is KtClassOrObject) { + for (delegateDecl in declaration.superTypeListEntries.filterIsInstance()) { + val delegateExpr = delegateDecl.delegateExpression ?: continue + if (bindingContext.getType(delegateExpr)?.isDynamic() == true) { + diagnosticHolder.report(ErrorsJs.DELEGATION_BY_DYNAMIC.on(delegateExpr)) + } + } + } + } +} \ No newline at end of file