Skip to content

Commit

Permalink
Support JDKs 16+
Browse files Browse the repository at this point in the history
  • Loading branch information
jrodbx committed Aug 3, 2022
1 parent 05ab3aa commit 137f5ca
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
# Also, see: https://github.com/touchlab/SQLiter/pull/38#issuecomment-867171789
ubuntu-18.04
]
java-version: [11, 12, 13, 14, 15]
java-version: [11, 12, 16, 18]

runs-on: ${{matrix.os}}

Expand Down
39 changes: 5 additions & 34 deletions paparazzi/paparazzi/src/main/java/app/cash/paparazzi/Paparazzi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import java.awt.image.BufferedImage
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import java.util.Date
import java.util.concurrent.TimeUnit
import kotlin.coroutines.ContinuationInterceptor
Expand Down Expand Up @@ -380,42 +378,15 @@ class Paparazzi @JvmOverloads constructor(
}

private fun forcePlatformSdkVersion(compileSdkVersion: Int) {
val modifiersField =
try {
Field::class.java.getDeclaredField("modifiers")
} catch (e: NoSuchFieldException) {
// Hack for Java 12+ access
// https://stackoverflow.com/q/56039341
// https://github.com/powermock/powermock/commit/66ce9f77215bae68b45f35481abc8b52a5d5b6ae#diff-21c1fc51058efd316026f11f34f51c5c
try {
val getDeclaredFields0 =
Class::class.java.getDeclaredMethod(
"getDeclaredFields0",
Boolean::class.javaPrimitiveType
)
getDeclaredFields0.isAccessible = true
val fields = getDeclaredFields0.invoke(Field::class.java, false) as Array<Field>
fields.find { it.name == "modifiers" } ?: throw e
} catch (ex: Exception) {
e.addSuppressed(ex)
throw e
}
}
modifiersField.isAccessible = true

val versionClass = try {
val buildVersionClass = try {
Paparazzi::class.java.classLoader.loadClass("android.os.Build\$VERSION")
} catch (e: ClassNotFoundException) {
// Project unit tests don't load Android platform code
return
}

versionClass
.getDeclaredField("SDK_INT")
.apply {
isAccessible = true
modifiersField.setInt(this, modifiers and Modifier.FINAL.inv())
setInt(null, compileSdkVersion)
}
buildVersionClass
.getFieldReflectively("SDK_INT")
.setStaticValue(compileSdkVersion)
}

private fun initializeAppCompatIfPresent() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package app.cash.paparazzi

import sun.misc.Unsafe
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import java.security.AccessController
import java.security.PrivilegedAction

/**
* Inspired by and ported from:
* https://github.com/powermock/powermock/commit/fc092c5d7e339d01e079184a2a0e88b5c46fc0e8
* https://github.com/powermock/powermock/commit/bd92bcc5329c4981cf09dece5c3eafcf92fe49ff
*/
internal fun Class<*>.getFieldReflectively(fieldName: String): Field =
try {
this.getDeclaredField(fieldName).also { it.isAccessible = true }
} catch (e: NoSuchFieldException) {
throw RuntimeException("Field '$fieldName' was not found in class $name.")
}

internal fun Field.setStaticValue(value: Any) {
try {
this.isAccessible = true
val isFinalModifierPresent = this.modifiers and Modifier.FINAL == Modifier.FINAL
if (isFinalModifierPresent) {
AccessController.doPrivileged<Any?>(
PrivilegedAction {
try {
val unsafe = Unsafe::class.java.getFieldReflectively("theUnsafe").get(null) as Unsafe
val offset = unsafe.staticFieldOffset(this)
val base = unsafe.staticFieldBase(this)
unsafe.setFieldValue(this, base, offset, value)
null
} catch (t: Throwable) {
throw RuntimeException(t)
}
}
)
} else {
this.set(null, value)
}
} catch (ex: SecurityException) {
throw RuntimeException(ex)
} catch (ex: IllegalAccessException) {
throw RuntimeException(ex)
} catch (ex: IllegalArgumentException) {
throw RuntimeException(ex)
}
}

internal fun Unsafe.setFieldValue(field: Field, base: Any, offset: Long, value: Any) =
when (field.type) {
Integer.TYPE -> this.putInt(base, offset, (value as Int))
java.lang.Short.TYPE -> this.putShort(base, offset, (value as Short))
java.lang.Long.TYPE -> this.putLong(base, offset, (value as Long))
java.lang.Byte.TYPE -> this.putByte(base, offset, (value as Byte))
java.lang.Boolean.TYPE -> this.putBoolean(base, offset, (value as Boolean))
java.lang.Float.TYPE -> this.putFloat(base, offset, (value as Float))
java.lang.Double.TYPE -> this.putDouble(base, offset, (value as Double))
Character.TYPE -> this.putChar(base, offset, (value as Char))
else -> this.putObject(base, offset, value)
}

0 comments on commit 137f5ca

Please sign in to comment.