Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Fixes

- Fix a D8 crash (`NullPointerException: Cannot invoke "String.length()"`) when dexing the SDK with AGP 8.0 and older, a regression from 8.61.1 ([#6356](https://github.com/getsentry/sentry-java/pull/6356))
- Always remove the first-draw listener after the first frame, even if no layout pass follows it ([#6152](https://github.com/getsentry/sentry-java/pull/6152))

## 8.61.1
Expand Down
1 change: 1 addition & 0 deletions build-logic/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ plugins {
dependencies {
implementation(libs.animalsniffer.gradle.plugin)
implementation(libs.spotlessLib)
implementation(libs.asm)
compileOnly(libs.kotlin.gradle.plugin)
compileOnly(libs.android.gradle.plugin)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ private val PUBLISHED_JAVA_VERSION = JavaVersion.VERSION_1_8
* - sample apps target the build JDK (21)
* - unit test code will target the build JDK (21) (required for Mockito 5)
* - published JVM modules main code pins the Java 1.8 release, for Java and for Kotlin
* - published modules Java code drops the MethodParameters attribute javac 21 adds
* - published Android modules main code targets Java 1.8
* - minSdk is set for Android libraries
*/
Expand All @@ -39,6 +40,10 @@ class JdkTargetConvention : Plugin<Project> {
}

project.plugins.withId("com.vanniktech.maven.publish") {
// Published Java classes must stay dexable by older D8 versions, see StripMethodParameters
project.tasks.withType(JavaCompile::class.java).configureEach {
doLast(StripMethodParameters())
}
// Only works for non-android modules
// See https://issuetracker.google.com/issues/278800528
project.plugins.withId("java-library") {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package io.sentry.gradle

import org.gradle.api.Action
import org.gradle.api.Task
import org.gradle.api.tasks.compile.JavaCompile
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes

/**
* Removes the
* [MethodParameters](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24)
* attribute from the classes a JavaCompile task produced.
*
* Since JDK 21 (JDK-8292275) javac writes MethodParameters for every method with synthetic or
* mandated parameters, e.g. bridge methods, even without `-parameters`. Those entries carry flags
* but no name, and the D8 of AGP 8.0 and older fails the build while dexing. See the
* [R8 fix](https://r8.googlesource.com/r8/+/9da58a26e22f195d8187492fac350a90e3315e92) for more
* details. An alternative would be adding the `-parameters` compile flag, but this could lead to
* other binary incompatibility issues along the road.
*
* The action removes every MethodParameters entry, also the named ones that `-parameters` writes.
* Keep it away from code that compiles with `-parameters`, e.g. the Spring Boot samples, where
* Spring resolves bean and handler parameters by name at runtime.
*/
internal class StripMethodParameters : Action<Task> {
override fun execute(task: Task) {
val classesDir = (task as JavaCompile).destinationDirectory.get().asFile
classesDir
.walkTopDown()
.filter { it.isFile && it.extension == "class" }
.forEach { file ->
val reader = ClassReader(file.readBytes())
val writer = ClassWriter(reader, 0)
val stripper = MethodParametersStripper(writer)
reader.accept(stripper, 0)
if (stripper.stripped) {
file.writeBytes(writer.toByteArray())
}
}
}

private class MethodParametersStripper(next: ClassVisitor) : ClassVisitor(Opcodes.ASM9, next) {
var stripped = false

override fun visitMethod(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
exceptions: Array<out String>?,
): MethodVisitor? {
val next = super.visitMethod(access, name, descriptor, signature, exceptions)
return object : MethodVisitor(Opcodes.ASM9, next) {
override fun visitParameter(name: String?, access: Int) {
stripped = true
}
}
}
}
}
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ lottie-compose = { module = "com.airbnb.android:lottie-compose", version = "6.7.
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "agp" }
asm = { module = "org.ow2.asm:asm", version = "9.9" }
nopen-annotations = { module = "com.jakewharton.nopen:nopen-annotations", version.ref = "nopen" }
nopen-checker = { module = "com.jakewharton.nopen:nopen-checker", version.ref = "nopen" }
nullaway = { module = "com.uber.nullaway:nullaway", version = "0.14.2" }
Expand Down
Loading