From 4c0274035355089b16e644ea57b43027bc7685d3 Mon Sep 17 00:00:00 2001 From: Stamatis Zampetakis Date: Fri, 3 May 2024 00:23:03 +0200 Subject: [PATCH] [CALCITE-6393] Add bytecode verification check using ASM in the gradle build --- build.gradle.kts | 11 +++++ buildSrc/settings.gradle.kts | 1 + .../asmchecker/asmchecker.gradle.kts | 34 +++++++++++++ .../buildtools/asmchecker/AsmCheckerPlugin.kt | 26 ++++++++++ .../buildtools/asmchecker/AsmCheckerTask.kt | 48 +++++++++++++++++++ 5 files changed, 120 insertions(+) create mode 100644 buildSrc/subprojects/asmchecker/asmchecker.gradle.kts create mode 100644 buildSrc/subprojects/asmchecker/src/main/kotlin/org/apache/calcite/buildtools/asmchecker/AsmCheckerPlugin.kt create mode 100644 buildSrc/subprojects/asmchecker/src/main/kotlin/org/apache/calcite/buildtools/asmchecker/AsmCheckerTask.kt diff --git a/build.gradle.kts b/build.gradle.kts index 0f4ff553db4..c5af658ed17 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,6 +27,7 @@ import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApisExtension import java.net.URI import net.ltgt.gradle.errorprone.errorprone +import org.apache.calcite.buildtools.asmchecker.AsmCheckerTask import org.apache.calcite.buildtools.buildext.dsl.ParenthesisBalancer import org.gradle.api.tasks.testing.logging.TestExceptionFormat @@ -37,6 +38,7 @@ plugins { publishing // Verification checkstyle + id("calcite.asmchecker") calcite.buildext jacoco id("jacoco-report-aggregation") @@ -844,6 +846,15 @@ allprojects { } jvmArgs("-Xmx6g") } + register("bytecodeCheck") { + group = LifecycleBasePlugin.VERIFICATION_GROUP + description = "Checks the bytecode of every .class file in the build directory using ASM." + dependsOn("classes") + } + + check { + dependsOn("bytecodeCheck") + } configureEach { group = LifecycleBasePlugin.VERIFICATION_GROUP if (enableSpotBugs) { diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index 1149c90d8d7..f35721b47b6 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -24,6 +24,7 @@ pluginManagement { } } +include("asmchecker") include("javacc") include("fmpp") include("buildext") diff --git a/buildSrc/subprojects/asmchecker/asmchecker.gradle.kts b/buildSrc/subprojects/asmchecker/asmchecker.gradle.kts new file mode 100644 index 00000000000..15761834662 --- /dev/null +++ b/buildSrc/subprojects/asmchecker/asmchecker.gradle.kts @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you 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. + */ + +dependencies { + val asmVersion = "9.6" + implementation("org.ow2.asm:asm:$asmVersion") + implementation("org.ow2.asm:asm-analysis:$asmVersion") + implementation("org.ow2.asm:asm-commons:$asmVersion") + implementation("org.ow2.asm:asm-tree:$asmVersion") + implementation("org.ow2.asm:asm-util:$asmVersion") +} + +gradlePlugin { + plugins { + register("asmchecker") { + id = "calcite.asmchecker" + implementationClass = "org.apache.calcite.buildtools.asmchecker.AsmCheckerPlugin" + } + } +} diff --git a/buildSrc/subprojects/asmchecker/src/main/kotlin/org/apache/calcite/buildtools/asmchecker/AsmCheckerPlugin.kt b/buildSrc/subprojects/asmchecker/src/main/kotlin/org/apache/calcite/buildtools/asmchecker/AsmCheckerPlugin.kt new file mode 100644 index 00000000000..22463b41cd0 --- /dev/null +++ b/buildSrc/subprojects/asmchecker/src/main/kotlin/org/apache/calcite/buildtools/asmchecker/AsmCheckerPlugin.kt @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you 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.apache.calcite.buildtools.asmchecker + +import org.gradle.api.Plugin +import org.gradle.api.Project + +open class AsmCheckerPlugin : Plugin { + override fun apply(target: Project) { + } +} diff --git a/buildSrc/subprojects/asmchecker/src/main/kotlin/org/apache/calcite/buildtools/asmchecker/AsmCheckerTask.kt b/buildSrc/subprojects/asmchecker/src/main/kotlin/org/apache/calcite/buildtools/asmchecker/AsmCheckerTask.kt new file mode 100644 index 00000000000..241b3ed9b26 --- /dev/null +++ b/buildSrc/subprojects/asmchecker/src/main/kotlin/org/apache/calcite/buildtools/asmchecker/AsmCheckerTask.kt @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you 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.apache.calcite.buildtools.asmchecker + +import java.nio.file.Files +import java.nio.file.Paths +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.TaskAction +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.commons.ClassRemapper +import org.objectweb.asm.commons.Remapper +import org.objectweb.asm.util.CheckClassAdapter + +@CacheableTask +open class AsmCheckerTask : DefaultTask() { + + @TaskAction + fun run() { + project.buildDir.walk().filter { file -> file.getName().toLowerCase().endsWith(".class") } + .forEach { + val classReader = ClassReader(Files.readAllBytes(Paths.get(it.getPath()))) + val classVisitor = CheckClassAdapter(ClassWriter(ClassWriter.COMPUTE_MAXS)) + val classRemapper = ClassRemapper(classVisitor, object : Remapper() {}) + try { + classReader.accept(classRemapper, ClassReader.EXPAND_FRAMES) + } catch (e: java.lang.RuntimeException) { + throw java.lang.RuntimeException("Invalid bytecode file:" + it, e) + } + } + } +}