diff --git a/.teamcity/subprojects.json b/.teamcity/subprojects.json index 2af51bd7e309..11e348a92e93 100644 --- a/.teamcity/subprojects.json +++ b/.teamcity/subprojects.json @@ -199,7 +199,7 @@ "name": "declarative-dsl-core", "path": "platforms/core-configuration/declarative-dsl-core", "unitTests": true, - "functionalTests": false, + "functionalTests": true, "crossVersionTests": false }, { diff --git a/platforms/core-configuration/declarative-dsl-core/build.gradle.kts b/platforms/core-configuration/declarative-dsl-core/build.gradle.kts index 19d38ab16e87..9ba55a0dc1be 100644 --- a/platforms/core-configuration/declarative-dsl-core/build.gradle.kts +++ b/platforms/core-configuration/declarative-dsl-core/build.gradle.kts @@ -28,4 +28,6 @@ dependencies { testImplementation(libs.futureKotlin("test-junit5")) testImplementation("org.jetbrains:annotations:24.0.1") + + integTestDistributionRuntimeOnly(project(":distributions-full")) } diff --git a/platforms/core-configuration/declarative-dsl-core/src/integTest/groovy/org/gradle/internal/declarativedsl/ErrorHandlingOnReflectiveCallsSpec.groovy b/platforms/core-configuration/declarative-dsl-core/src/integTest/groovy/org/gradle/internal/declarativedsl/ErrorHandlingOnReflectiveCallsSpec.groovy new file mode 100644 index 000000000000..322d6786ba84 --- /dev/null +++ b/platforms/core-configuration/declarative-dsl-core/src/integTest/groovy/org/gradle/internal/declarativedsl/ErrorHandlingOnReflectiveCallsSpec.groovy @@ -0,0 +1,111 @@ +/* + * Copyright 2024 the original author or authors. + * + * 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.gradle.internal.declarativedsl + +import org.gradle.integtests.fixtures.AbstractIntegrationSpec + +class ErrorHandlingOnReflectiveCallsSpec extends AbstractIntegrationSpec { + + def 'when reflective invocation fails the cause is identified correctly'() { + given: + file("buildSrc/build.gradle") << """ + plugins { + id('java-gradle-plugin') + } + gradlePlugin { + plugins { + create("restrictedPlugin") { + id = "com.example.restricted" + implementationClass = "com.example.restricted.RestrictedPlugin" + } + } + } + """ + + file("buildSrc/src/main/java/com/example/restricted/Extension.java") << """ + package com.example.restricted; + + import org.gradle.declarative.dsl.model.annotations.Configuring; + import org.gradle.declarative.dsl.model.annotations.Restricted; + import org.gradle.api.Action; + import org.gradle.api.model.ObjectFactory; + import org.gradle.api.provider.Property; + + import javax.inject.Inject; + + @Restricted + public abstract class Extension { + private final Access access; + private final ObjectFactory objects; + + public Access getAccess() { + return access; + } + + @Inject + public Extension(ObjectFactory objects) { + this.objects = objects; + this.access = objects.newInstance(Access.class); + } + + @Configuring + public void access(Action configure) { + throw new RuntimeException("Boom"); + } + + public abstract static class Access { + @Restricted + public abstract Property getName(); + } + + } + """ + + file("buildSrc/src/main/java/com/example/restricted/RestrictedPlugin.java") << """ + package com.example.restricted; + + import org.gradle.api.Plugin; + import org.gradle.api.Project; + + public class RestrictedPlugin implements Plugin { + @Override + public void apply(Project target) { + target.getExtensions().create("restricted", Extension.class); + } + } + """ + + file("build.gradle.something") << """ + plugins { + id("com.example.restricted") + } + + restricted { + access { + name = "something" + } + } + """ + + when: + fails(":help") + + then: + failureCauseContains("Boom") + } + +} diff --git a/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/DeclarativeReflectionToObjectConverter.kt b/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/DeclarativeReflectionToObjectConverter.kt index 9663436cd458..ec333481ba9b 100644 --- a/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/DeclarativeReflectionToObjectConverter.kt +++ b/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/DeclarativeReflectionToObjectConverter.kt @@ -117,13 +117,13 @@ class DeclarativeReflectionToObjectConverter( fun invokeFunctionAndGetResult( receiverInstance: Any, origin: ObjectOrigin.FunctionInvocationOrigin - ): RestrictedRuntimeFunction.InvocationResult { + ): DeclarativeRuntimeFunction.InvocationResult { val dataFun = origin.function val receiverKClass = receiverInstance::class return when (val runtimeFunction = functionResolver.resolve(receiverKClass, dataFun.simpleName, origin.parameterBindings)) { is RuntimeFunctionResolver.Resolution.Resolved -> { val bindingWithValues = origin.parameterBindings.bindingMap.mapValues { getObjectByResolvedOrigin(it.value) } - runtimeFunction.function.callBy(receiverInstance, bindingWithValues, origin.parameterBindings.providesConfigureBlock) + runtimeFunction.function.callByWithErrorHandling(receiverInstance, bindingWithValues, origin.parameterBindings.providesConfigureBlock) } RuntimeFunctionResolver.Resolution.Unresolved -> error("could not resolve a member function $dataFun call in the owner class $receiverKClass") @@ -150,7 +150,7 @@ class DeclarativeReflectionToObjectConverter( when (val runtimeFunction = functionResolver.resolve(receiverKClass, function.simpleName, parameterBinding)) { is RuntimeFunctionResolver.Resolution.Resolved -> - runtimeFunction.function.callBy(receiverInstance, parameterBinding.bindingMap.mapValues { getObjectByResolvedOrigin(it.value) }, parameterBinding.providesConfigureBlock).result + runtimeFunction.function.callByWithErrorHandling(receiverInstance, parameterBinding.bindingMap.mapValues { getObjectByResolvedOrigin(it.value) }, parameterBinding.providesConfigureBlock).result RuntimeFunctionResolver.Resolution.Unresolved -> error("could not resolve a member function $function call in the owner class $receiverKClass") } } diff --git a/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RestrictedRuntimeFunction.kt b/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/DeclarativeRuntimeFunction.kt similarity index 68% rename from platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RestrictedRuntimeFunction.kt rename to platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/DeclarativeRuntimeFunction.kt index 3a3e37f8906f..0c8f734bfd6b 100644 --- a/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RestrictedRuntimeFunction.kt +++ b/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/DeclarativeRuntimeFunction.kt @@ -18,22 +18,31 @@ package org.gradle.internal.declarativedsl.mappingToJvm import org.gradle.internal.declarativedsl.analysis.DataParameter import org.gradle.internal.declarativedsl.schemaBuilder.ConfigureLambdaHandler +import java.lang.reflect.InvocationTargetException import kotlin.reflect.KFunction -interface RestrictedRuntimeFunction { +interface DeclarativeRuntimeFunction { fun callBy(receiver: Any, binding: Map, hasLambda: Boolean): InvocationResult + fun callByWithErrorHandling(receiver: Any, binding: Map, hasLambda: Boolean): InvocationResult { + try { + return callBy(receiver, binding, hasLambda) + } catch (ite: InvocationTargetException) { + throw ite.cause ?: ite + } + } + data class InvocationResult(val result: Any?, val capturedValue: Any?) } internal -class ReflectionFunction(private val kFunction: KFunction<*>, private val configureLambdaHandler: ConfigureLambdaHandler) : RestrictedRuntimeFunction { - override fun callBy(receiver: Any, binding: Map, hasLambda: Boolean): RestrictedRuntimeFunction.InvocationResult { +class ReflectionFunction(private val kFunction: KFunction<*>, private val configureLambdaHandler: ConfigureLambdaHandler) : DeclarativeRuntimeFunction { + override fun callBy(receiver: Any, binding: Map, hasLambda: Boolean): DeclarativeRuntimeFunction.InvocationResult { val params = FunctionBinding.convertBinding(kFunction, receiver, binding, hasLambda, configureLambdaHandler) ?: error("signature of $kFunction does not match the arguments: $binding") val captor = params.valueCaptor - return RestrictedRuntimeFunction.InvocationResult(kFunction.callBy(params.map), captor?.value) + return DeclarativeRuntimeFunction.InvocationResult(kFunction.callBy(params.map), captor?.value) } } diff --git a/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RestrictedRuntimeProperty.kt b/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/DeclarativeRuntimeProperty.kt similarity index 95% rename from platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RestrictedRuntimeProperty.kt rename to platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/DeclarativeRuntimeProperty.kt index 190b98d0bca7..585ff5251cc0 100644 --- a/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RestrictedRuntimeProperty.kt +++ b/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/DeclarativeRuntimeProperty.kt @@ -17,7 +17,7 @@ package org.gradle.internal.declarativedsl.mappingToJvm -interface RestrictedRuntimeProperty { +interface DeclarativeRuntimeProperty { fun getValue(receiver: Any): Any? fun setValue(receiver: Any, value: Any?) } diff --git a/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RuntimeFunctionResolver.kt b/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RuntimeFunctionResolver.kt index dc74499bb961..eee2b95720fa 100644 --- a/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RuntimeFunctionResolver.kt +++ b/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RuntimeFunctionResolver.kt @@ -26,7 +26,7 @@ interface RuntimeFunctionResolver { fun resolve(receiverClass: KClass<*>, name: String, parameterValueBinding: ParameterValueBinding): Resolution sealed interface Resolution { - data class Resolved(val function: RestrictedRuntimeFunction) : Resolution + data class Resolved(val function: DeclarativeRuntimeFunction) : Resolution data object Unresolved : Resolution } } diff --git a/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RuntimePropertyResolver.kt b/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RuntimePropertyResolver.kt index 777c38657f3c..5f229d52bf84 100644 --- a/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RuntimePropertyResolver.kt +++ b/platforms/core-configuration/declarative-dsl-core/src/main/kotlin/org/gradle/internal/declarativedsl/mappingToJvm/RuntimePropertyResolver.kt @@ -30,7 +30,7 @@ interface RuntimePropertyResolver { fun resolvePropertyWrite(receiverClass: KClass<*>, name: String): Resolution sealed interface Resolution { - data class Resolved(val property: RestrictedRuntimeProperty) : Resolution + data class Resolved(val property: DeclarativeRuntimeProperty) : Resolution data object Unresolved : Resolution } } @@ -43,7 +43,7 @@ object ReflectionRuntimePropertyResolver : RuntimePropertyResolver { return when (callable) { null -> Unresolved - else -> Resolved(object : RestrictedRuntimeProperty { + else -> Resolved(object : DeclarativeRuntimeProperty { override fun getValue(receiver: Any): Any? = callable.call(receiver) override fun setValue(receiver: Any, value: Any?) = throw UnsupportedOperationException() }) @@ -56,7 +56,7 @@ object ReflectionRuntimePropertyResolver : RuntimePropertyResolver { return when (setter) { null -> Unresolved - else -> Resolved(object : RestrictedRuntimeProperty { + else -> Resolved(object : DeclarativeRuntimeProperty { override fun getValue(receiver: Any): Any = throw UnsupportedOperationException() override fun setValue(receiver: Any, value: Any?) { setter.call(receiver, value) diff --git a/platforms/core-configuration/declarative-dsl-provider/src/main/kotlin/org/gradle/internal/declarativedsl/project/TypeSafeProjectAccessorsSchemaBuildingComponent.kt b/platforms/core-configuration/declarative-dsl-provider/src/main/kotlin/org/gradle/internal/declarativedsl/project/TypeSafeProjectAccessorsSchemaBuildingComponent.kt index 2f3dc77ad0ab..ab6401440ea2 100644 --- a/platforms/core-configuration/declarative-dsl-provider/src/main/kotlin/org/gradle/internal/declarativedsl/project/TypeSafeProjectAccessorsSchemaBuildingComponent.kt +++ b/platforms/core-configuration/declarative-dsl-provider/src/main/kotlin/org/gradle/internal/declarativedsl/project/TypeSafeProjectAccessorsSchemaBuildingComponent.kt @@ -22,7 +22,7 @@ import org.gradle.internal.declarativedsl.analysis.DataProperty import org.gradle.internal.declarativedsl.analysis.DataTypeRef import org.gradle.internal.declarativedsl.analysis.FqName import org.gradle.internal.declarativedsl.evaluationSchema.EvaluationSchemaComponent -import org.gradle.internal.declarativedsl.mappingToJvm.RestrictedRuntimeProperty +import org.gradle.internal.declarativedsl.mappingToJvm.DeclarativeRuntimeProperty import org.gradle.internal.declarativedsl.mappingToJvm.RuntimePropertyResolver import org.gradle.internal.declarativedsl.schemaBuilder.CollectedPropertyInformation import org.gradle.internal.declarativedsl.schemaBuilder.DefaultPropertyExtractor @@ -89,7 +89,7 @@ private class ProjectPropertyAccessorRuntimeResolver : RuntimePropertyResolver { override fun resolvePropertyRead(receiverClass: KClass<*>, name: String): RuntimePropertyResolver.Resolution { if (receiverClass.isSubclassOf(Project::class) && name == "projects") { - return RuntimePropertyResolver.Resolution.Resolved(object : RestrictedRuntimeProperty { + return RuntimePropertyResolver.Resolution.Resolved(object : DeclarativeRuntimeProperty { override fun getValue(receiver: Any) = (receiver as Project).extensions.getByName("projects") override fun setValue(receiver: Any, value: Any?): Unit = throw UnsupportedOperationException() }) diff --git a/platforms/core-configuration/declarative-dsl-provider/src/main/kotlin/org/gradle/internal/declarativedsl/project/dependencyConfigurationSchema.kt b/platforms/core-configuration/declarative-dsl-provider/src/main/kotlin/org/gradle/internal/declarativedsl/project/dependencyConfigurationSchema.kt index e0a310370f50..438e347e337f 100644 --- a/platforms/core-configuration/declarative-dsl-provider/src/main/kotlin/org/gradle/internal/declarativedsl/project/dependencyConfigurationSchema.kt +++ b/platforms/core-configuration/declarative-dsl-provider/src/main/kotlin/org/gradle/internal/declarativedsl/project/dependencyConfigurationSchema.kt @@ -24,7 +24,7 @@ import org.gradle.internal.declarativedsl.analysis.FunctionSemantics import org.gradle.internal.declarativedsl.analysis.ParameterSemantics import org.gradle.internal.declarativedsl.analysis.ParameterValueBinding import org.gradle.internal.declarativedsl.analysis.SchemaMemberFunction -import org.gradle.internal.declarativedsl.mappingToJvm.RestrictedRuntimeFunction +import org.gradle.internal.declarativedsl.mappingToJvm.DeclarativeRuntimeFunction import org.gradle.internal.declarativedsl.mappingToJvm.RuntimeFunctionResolver import org.gradle.internal.declarativedsl.schemaBuilder.DataSchemaBuilder import org.gradle.internal.declarativedsl.schemaBuilder.FunctionExtractor @@ -126,10 +126,10 @@ class RuntimeDependencyFunctionResolver(configurations: DependencyConfigurations override fun resolve(receiverClass: KClass<*>, name: String, parameterValueBinding: ParameterValueBinding): RuntimeFunctionResolver.Resolution { if (receiverClass.isSubclassOf(DependencyHandler::class) && name in nameSet && parameterValueBinding.bindingMap.size == 1) { - return RuntimeFunctionResolver.Resolution.Resolved(object : RestrictedRuntimeFunction { - override fun callBy(receiver: Any, binding: Map, hasLambda: Boolean): RestrictedRuntimeFunction.InvocationResult { + return RuntimeFunctionResolver.Resolution.Resolved(object : DeclarativeRuntimeFunction { + override fun callBy(receiver: Any, binding: Map, hasLambda: Boolean): DeclarativeRuntimeFunction.InvocationResult { (receiver as DependencyHandler).add(name, binding.values.single() ?: error("null value in dependency DSL")) - return RestrictedRuntimeFunction.InvocationResult(Unit, null) + return DeclarativeRuntimeFunction.InvocationResult(Unit, null) } }) } @@ -148,12 +148,12 @@ class ImplicitDependencyCollectorFunctionResolver(configurations: DependencyConf if (name in configurationNames) { val getterFunction = getDependencyCollectorGetter(receiverClass, name) if (getterFunction != null) { - return RuntimeFunctionResolver.Resolution.Resolved(object : RestrictedRuntimeFunction { - override fun callBy(receiver: Any, binding: Map, hasLambda: Boolean): RestrictedRuntimeFunction.InvocationResult { + return RuntimeFunctionResolver.Resolution.Resolved(object : DeclarativeRuntimeFunction { + override fun callBy(receiver: Any, binding: Map, hasLambda: Boolean): DeclarativeRuntimeFunction.InvocationResult { val dependencyNotation = binding.values.single().toString() val collector: DependencyCollector = getterFunction.call(receiver) as DependencyCollector collector.add(dependencyNotation) - return RestrictedRuntimeFunction.InvocationResult(Unit, null) + return DeclarativeRuntimeFunction.InvocationResult(Unit, null) } }) }