mirror of
https://github.com/corda/corda.git
synced 2024-12-24 07:06:44 +00:00
CORDA-1699: Remove parameter defaults from Kotlin constructor metadata (#3542)
* Remove unwanted JavaScript metadata files from kotlin-metadata. * Rename SanitiseConstructor case to SanitiseDeleteConstructor. * Also remove default parameter flags from primary constructor when deleting the synthetic constructor that handles them. Add a test case for stubbing constructors with default parameters. * Restore deterministic modules' dependency on assemble task.
This commit is contained in:
parent
f4426ef172
commit
da32a7e56f
@ -26,7 +26,7 @@ def originalJar = configurations.proguard.files.find { it.name.startsWith("kotli
|
||||
|
||||
import proguard.gradle.ProGuardTask
|
||||
task metadata(type: ProGuardTask) {
|
||||
injars originalJar, filter: '!META-INF/native/**'
|
||||
injars originalJar, filter: 'META-INF/MANIFEST.MF,META-INF/metadata*.kotlin_module,**.class'
|
||||
outjars "$buildDir/libs/${project.name}-${kotlin_version}.jar"
|
||||
|
||||
libraryjars "$javaHome/lib/rt.jar"
|
||||
@ -49,7 +49,10 @@ task metadata(type: ProGuardTask) {
|
||||
dontnote
|
||||
|
||||
keep 'class org.jetbrains.kotlin.load.java.JvmAnnotationNames { *; }'
|
||||
keep 'class org.jetbrains.kotlin.metadata.** { *; }', includedescriptorclasses: true
|
||||
keep 'class org.jetbrains.kotlin.metadata.ProtoBuf { *; }', includedescriptorclasses: true
|
||||
keep 'class org.jetbrains.kotlin.metadata.ProtoBuf$* { *; }', includedescriptorclasses: true
|
||||
keep 'class org.jetbrains.kotlin.metadata.deserialization.** { *; }', includedescriptorclasses: true
|
||||
keep 'class org.jetbrains.kotlin.metadata.jvm.** { *; }', includedescriptorclasses: true
|
||||
keep 'class org.jetbrains.kotlin.protobuf.** { *; }', includedescriptorclasses: true
|
||||
}
|
||||
def metadataJar = metadata.outputs.files.singleFile
|
||||
|
@ -2,16 +2,21 @@
|
||||
package net.corda.gradle.jarfilter
|
||||
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.Flags.*
|
||||
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
|
||||
import org.jetbrains.kotlin.metadata.deserialization.TypeTable
|
||||
import org.jetbrains.kotlin.metadata.deserialization.returnType
|
||||
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
||||
import org.objectweb.asm.Opcodes.ACC_SYNTHETIC
|
||||
import java.util.*
|
||||
|
||||
private const val DEFAULT_CONSTRUCTOR_MARKER = "ILkotlin/jvm/internal/DefaultConstructorMarker;"
|
||||
private const val DUMMY_PASSES = 1
|
||||
|
||||
private val DECLARES_DEFAULT_VALUE_MASK: Int = DECLARES_DEFAULT_VALUE.toFlags(true).inv()
|
||||
|
||||
internal abstract class Element(val name: String, val descriptor: String) {
|
||||
private var lifetime: Int = DUMMY_PASSES
|
||||
|
||||
@ -36,6 +41,7 @@ internal class MethodElement(name: String, descriptor: String, val access: Int =
|
||||
|
||||
private val suffix: String
|
||||
val visibleName: String
|
||||
val signature: String = name + descriptor
|
||||
|
||||
init {
|
||||
val idx = name.indexOf('$')
|
||||
@ -44,6 +50,14 @@ internal class MethodElement(name: String, descriptor: String, val access: Int =
|
||||
}
|
||||
|
||||
fun isKotlinSynthetic(vararg tags: String): Boolean = (access and ACC_SYNTHETIC) != 0 && tags.contains(suffix)
|
||||
fun asKotlinNonDefaultConstructor(): MethodElement? {
|
||||
val markerIdx = descriptor.indexOf(DEFAULT_CONSTRUCTOR_MARKER)
|
||||
return if (markerIdx >= 0) {
|
||||
MethodElement(name, descriptor.removeRange(markerIdx, markerIdx + DEFAULT_CONSTRUCTOR_MARKER.length))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -107,4 +121,24 @@ internal fun JvmProtoBuf.JvmPropertySignature.toFieldElement(property: ProtoBuf.
|
||||
}
|
||||
|
||||
return FieldElement(nameResolver.getString(nameId), descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites metadata for constructor parameters.
|
||||
*/
|
||||
internal fun ProtoBuf.Constructor.Builder.updateValueParameters(
|
||||
updater: (ProtoBuf.ValueParameter) -> ProtoBuf.ValueParameter
|
||||
): ProtoBuf.Constructor.Builder {
|
||||
for (idx in 0 until valueParameterList.size) {
|
||||
setValueParameter(idx, updater(valueParameterList[idx]))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
internal fun ProtoBuf.ValueParameter.clearDeclaresDefaultValue(): ProtoBuf.ValueParameter {
|
||||
return if (DECLARES_DEFAULT_VALUE.get(flags)) {
|
||||
toBuilder().setFlags(flags and DECLARES_DEFAULT_VALUE_MASK).build()
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ class FilterTransformer private constructor (
|
||||
logger.info("-- also identified property or typealias {},{} for deletion", method.visibleName, extensionType)
|
||||
}
|
||||
}
|
||||
} else if (stubAnnotations.contains(descriptor) && (method.access and (ACC_ABSTRACT or ACC_SYNTHETIC)) == 0) {
|
||||
} else if (stubAnnotations.contains(descriptor) && (method.access and ACC_ABSTRACT) == 0) {
|
||||
if (stubbedMethods.add(method)) {
|
||||
logger.info("- Identified method {}{} for stubbing out", method.name, method.descriptor)
|
||||
}
|
||||
|
@ -95,17 +95,30 @@ internal abstract class MetadataTransformer<out T : MessageLite>(
|
||||
private fun filterConstructors(): Int = deletedConstructors.count(::filterConstructor)
|
||||
|
||||
private fun filterConstructor(deleted: MethodElement): Boolean {
|
||||
/*
|
||||
* Constructors with the default parameter marker are synthetic and DO NOT have
|
||||
* entries in the metadata. So we construct an element for the "primary" one
|
||||
* that it was synthesised for, and which we DO expect to find.
|
||||
*/
|
||||
val deletedPrimary = deleted.asKotlinNonDefaultConstructor()
|
||||
|
||||
for (idx in 0 until constructors.size) {
|
||||
val constructor = constructors[idx]
|
||||
val signature = JvmProtoBufUtil.getJvmConstructorSignature(constructor, nameResolver, typeTable)
|
||||
if (signature == deleted.name + deleted.descriptor) {
|
||||
if (signature == deleted.signature) {
|
||||
if (IS_SECONDARY.get(constructor.flags)) {
|
||||
logger.info("-- removing constructor: {}{}", deleted.name, deleted.descriptor)
|
||||
logger.info("-- removing constructor: {}", deleted.signature)
|
||||
} else {
|
||||
logger.warn("Removing primary constructor: {}{}", className, deleted.descriptor)
|
||||
}
|
||||
constructors.removeAt(idx)
|
||||
return true
|
||||
} else if (signature == deletedPrimary?.signature) {
|
||||
constructors[idx] = constructor.toBuilder()
|
||||
.updateValueParameters(ProtoBuf.ValueParameter::clearDeclaresDefaultValue)
|
||||
.build()
|
||||
logger.info("-- removing default parameter values: {}", signature)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
@ -118,8 +131,8 @@ internal abstract class MetadataTransformer<out T : MessageLite>(
|
||||
val function = functions[idx]
|
||||
if (nameResolver.getString(function.name) == deleted.name) {
|
||||
val signature = JvmProtoBufUtil.getJvmMethodSignature(function, nameResolver, typeTable)
|
||||
if (signature == deleted.name + deleted.descriptor) {
|
||||
logger.info("-- removing function: {}{}", deleted.name, deleted.descriptor)
|
||||
if (signature == deleted.signature) {
|
||||
logger.info("-- removing function: {}", deleted.signature)
|
||||
functions.removeAt(idx)
|
||||
return true
|
||||
}
|
||||
@ -157,7 +170,7 @@ internal abstract class MetadataTransformer<out T : MessageLite>(
|
||||
|
||||
private fun deleteExtra(func: MethodElement) {
|
||||
if (!deletedFunctions.contains(func)) {
|
||||
logger.info("-- identified extra method {}{} for deletion", func.name, func.descriptor)
|
||||
logger.info("-- identified extra method {} for deletion", func.signature)
|
||||
handleExtraMethod(func)
|
||||
filterFunction(func)
|
||||
}
|
||||
@ -244,12 +257,11 @@ internal class ClassMetadataTransformer(
|
||||
override val typeAliases = mutableList(message.typeAliasList)
|
||||
|
||||
override fun rebuild(): ProtoBuf.Class = message.toBuilder().apply {
|
||||
clearConstructor().addAllConstructor(constructors)
|
||||
|
||||
if (nestedClassNames.size != nestedClassNameCount) {
|
||||
clearNestedClassName().addAllNestedClassName(nestedClassNames)
|
||||
}
|
||||
if (constructors.size != constructorCount) {
|
||||
clearConstructor().addAllConstructor(constructors)
|
||||
}
|
||||
if (functions.size != functionCount) {
|
||||
clearFunction().addAllFunction(functions)
|
||||
}
|
||||
|
@ -16,12 +16,13 @@ import kotlin.jvm.kotlin
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class SanitiseConstructorTest {
|
||||
class SanitiseDeleteConstructorTest {
|
||||
companion object {
|
||||
private const val COMPLEX_CONSTRUCTOR_CLASS = "net.corda.gradle.HasOverloadedComplexConstructorToDelete"
|
||||
private const val COUNT_INITIAL_OVERLOADED = 1
|
||||
private const val COUNT_INITIAL_MULTIPLE = 2
|
||||
private val testProjectDir = TemporaryFolder()
|
||||
private val testProject = JarFilterProject(testProjectDir, "sanitise-constructor")
|
||||
private val testProject = JarFilterProject(testProjectDir, "sanitise-delete-constructor")
|
||||
|
||||
@ClassRule
|
||||
@JvmField
|
||||
@ -32,13 +33,13 @@ class SanitiseConstructorTest {
|
||||
|
||||
@Test
|
||||
fun deleteOverloadedLongConstructor() = checkClassWithLongParameter(
|
||||
"net.corda.gradle.HasOverloadedLongConstructor",
|
||||
"net.corda.gradle.HasOverloadedLongConstructorToDelete",
|
||||
COUNT_INITIAL_OVERLOADED
|
||||
)
|
||||
|
||||
@Test
|
||||
fun deleteMultipleLongConstructor() = checkClassWithLongParameter(
|
||||
"net.corda.gradle.HasMultipleLongConstructors",
|
||||
"net.corda.gradle.HasMultipleLongConstructorsToDelete",
|
||||
COUNT_INITIAL_MULTIPLE
|
||||
)
|
||||
|
||||
@ -57,9 +58,9 @@ class SanitiseConstructorTest {
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
assertThat(primary.call(BIG_NUMBER).longData()).isEqualTo(BIG_NUMBER)
|
||||
|
||||
newInstance().also {
|
||||
assertEquals(0, it.longData())
|
||||
}
|
||||
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||
assertThat(noArg.callBy(emptyMap()).longData()).isEqualTo(0)
|
||||
assertThat(newInstance().longData()).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,6 +76,7 @@ class SanitiseConstructorTest {
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
assertThat(primary.call(BIG_NUMBER).longData()).isEqualTo(BIG_NUMBER)
|
||||
|
||||
assertNull("no-arg constructor exists", kotlin.noArgConstructor)
|
||||
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor() }
|
||||
}
|
||||
}
|
||||
@ -82,13 +84,13 @@ class SanitiseConstructorTest {
|
||||
|
||||
@Test
|
||||
fun deleteOverloadedIntConstructor() = checkClassWithIntParameter(
|
||||
"net.corda.gradle.HasOverloadedIntConstructor",
|
||||
"net.corda.gradle.HasOverloadedIntConstructorToDelete",
|
||||
COUNT_INITIAL_OVERLOADED
|
||||
)
|
||||
|
||||
@Test
|
||||
fun deleteMultipleIntConstructor() = checkClassWithIntParameter(
|
||||
"net.corda.gradle.HasMultipleIntConstructors",
|
||||
"net.corda.gradle.HasMultipleIntConstructorsToDelete",
|
||||
COUNT_INITIAL_MULTIPLE
|
||||
)
|
||||
|
||||
@ -107,10 +109,9 @@ class SanitiseConstructorTest {
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
assertThat(primary.call(NUMBER).intData()).isEqualTo(NUMBER)
|
||||
|
||||
//assertThat("", constructors, hasItem(isConstructor(""))
|
||||
newInstance().also {
|
||||
assertEquals(0, it.intData())
|
||||
}
|
||||
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||
assertThat(noArg.callBy(emptyMap()).intData()).isEqualTo(0)
|
||||
assertThat(newInstance().intData()).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,6 +127,7 @@ class SanitiseConstructorTest {
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
assertThat(primary.call(NUMBER).intData()).isEqualTo(NUMBER)
|
||||
|
||||
assertNull("no-arg constructor exists", kotlin.noArgConstructor)
|
||||
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor() }
|
||||
}
|
||||
}
|
||||
@ -133,13 +135,13 @@ class SanitiseConstructorTest {
|
||||
|
||||
@Test
|
||||
fun deleteOverloadedStringConstructor() = checkClassWithStringParameter(
|
||||
"net.corda.gradle.HasOverloadedStringConstructor",
|
||||
"net.corda.gradle.HasOverloadedStringConstructorToDelete",
|
||||
COUNT_INITIAL_OVERLOADED
|
||||
)
|
||||
|
||||
@Test
|
||||
fun deleteMultipleStringConstructor() = checkClassWithStringParameter(
|
||||
"net.corda.gradle.HasMultipleStringConstructors",
|
||||
"net.corda.gradle.HasMultipleStringConstructorsToDelete",
|
||||
COUNT_INITIAL_MULTIPLE
|
||||
)
|
||||
|
||||
@ -158,9 +160,9 @@ class SanitiseConstructorTest {
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
assertThat(primary.call(MESSAGE).stringData()).isEqualTo(MESSAGE)
|
||||
|
||||
newInstance().also {
|
||||
assertEquals(DEFAULT_MESSAGE, it.stringData())
|
||||
}
|
||||
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||
assertThat(noArg.callBy(emptyMap()).stringData()).isEqualTo(DEFAULT_MESSAGE)
|
||||
assertThat(newInstance().stringData()).isEqualTo(DEFAULT_MESSAGE)
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,8 +178,57 @@ class SanitiseConstructorTest {
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
assertThat(primary.call(MESSAGE).stringData()).isEqualTo(MESSAGE)
|
||||
|
||||
assertNull("no-arg constructor exists", kotlin.noArgConstructor)
|
||||
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteOverloadedComplexConstructor() {
|
||||
val complexConstructor = isConstructor(COMPLEX_CONSTRUCTOR_CLASS, Int::class, String::class)
|
||||
|
||||
classLoaderFor(testProject.sourceJar).use { cl ->
|
||||
cl.load<Any>(COMPLEX_CONSTRUCTOR_CLASS).apply {
|
||||
kotlin.constructors.apply {
|
||||
assertThat("<init>(Int,String) not found", this, hasItem(complexConstructor))
|
||||
assertEquals(1, this.size)
|
||||
}
|
||||
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
primary.call(NUMBER, MESSAGE).also { complex ->
|
||||
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||
assertThat((complex as HasInt).intData()).isEqualTo(NUMBER)
|
||||
}
|
||||
|
||||
primary.callBy(mapOf(primary.parameters[1] to MESSAGE)).also { complex ->
|
||||
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||
assertThat((complex as HasInt).intData()).isEqualTo(0)
|
||||
}
|
||||
getDeclaredConstructor(String::class.java).newInstance(MESSAGE).also { complex ->
|
||||
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||
assertThat((complex as HasInt).intData()).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
classLoaderFor(testProject.filteredJar).use { cl ->
|
||||
cl.load<Any>(COMPLEX_CONSTRUCTOR_CLASS).apply {
|
||||
kotlin.constructors.apply {
|
||||
assertThat("<init>(Int,String) not found", this, hasItem(complexConstructor))
|
||||
assertEquals(1, this.size)
|
||||
}
|
||||
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
primary.call(NUMBER, MESSAGE).also { complex ->
|
||||
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||
assertThat((complex as HasInt).intData()).isEqualTo(NUMBER)
|
||||
}
|
||||
|
||||
assertThat(assertFailsWith<IllegalArgumentException> { primary.callBy(mapOf(primary.parameters[1] to MESSAGE)) })
|
||||
.hasMessageContaining("No argument provided for a required parameter")
|
||||
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor(String::class.java) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,250 @@
|
||||
package net.corda.gradle.jarfilter
|
||||
|
||||
import net.corda.gradle.jarfilter.matcher.*
|
||||
import net.corda.gradle.unwanted.HasInt
|
||||
import net.corda.gradle.unwanted.HasLong
|
||||
import net.corda.gradle.unwanted.HasString
|
||||
import org.assertj.core.api.Assertions.*
|
||||
import org.hamcrest.core.IsCollectionContaining.hasItem
|
||||
import org.junit.Assert.*
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.RuleChain
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import org.junit.rules.TestRule
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import kotlin.jvm.kotlin
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class SanitiseStubConstructorTest {
|
||||
companion object {
|
||||
private const val COMPLEX_CONSTRUCTOR_CLASS = "net.corda.gradle.HasOverloadedComplexConstructorToStub"
|
||||
private const val COUNT_OVERLOADED = 1
|
||||
private const val COUNT_MULTIPLE = 2
|
||||
private val testProjectDir = TemporaryFolder()
|
||||
private val testProject = JarFilterProject(testProjectDir, "sanitise-stub-constructor")
|
||||
|
||||
@ClassRule
|
||||
@JvmField
|
||||
val rules: TestRule = RuleChain
|
||||
.outerRule(testProjectDir)
|
||||
.around(testProject)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun stubOverloadedLongConstructor() = checkClassWithLongParameter(
|
||||
"net.corda.gradle.HasOverloadedLongConstructorToStub",
|
||||
COUNT_OVERLOADED
|
||||
)
|
||||
|
||||
@Test
|
||||
fun stubMultipleLongConstructor() = checkClassWithLongParameter(
|
||||
"net.corda.gradle.HasMultipleLongConstructorsToStub",
|
||||
COUNT_MULTIPLE
|
||||
)
|
||||
|
||||
private fun checkClassWithLongParameter(longClass: String, constructorCount: Int) {
|
||||
val longConstructor = isConstructor(longClass, Long::class)
|
||||
|
||||
classLoaderFor(testProject.sourceJar).use { cl ->
|
||||
cl.load<HasLong>(longClass).apply {
|
||||
getDeclaredConstructor(Long::class.java).newInstance(BIG_NUMBER).also {
|
||||
assertEquals(BIG_NUMBER, it.longData())
|
||||
}
|
||||
kotlin.constructors.apply {
|
||||
assertThat("<init>(J) not found", this, hasItem(longConstructor))
|
||||
assertEquals(constructorCount, this.size)
|
||||
}
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
assertThat(primary.call(BIG_NUMBER).longData()).isEqualTo(BIG_NUMBER)
|
||||
|
||||
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||
assertThat(noArg.callBy(emptyMap()).longData()).isEqualTo(0)
|
||||
assertThat(newInstance().longData()).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
|
||||
classLoaderFor(testProject.filteredJar).use { cl ->
|
||||
cl.load<HasLong>(longClass).apply {
|
||||
getDeclaredConstructor(Long::class.java).newInstance(BIG_NUMBER).also {
|
||||
assertEquals(BIG_NUMBER, it.longData())
|
||||
}
|
||||
kotlin.constructors.apply {
|
||||
assertThat("<init>(J) not found", this, hasItem(longConstructor))
|
||||
assertEquals(constructorCount, this.size)
|
||||
}
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
assertThat(primary.call(BIG_NUMBER).longData()).isEqualTo(BIG_NUMBER)
|
||||
|
||||
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||
assertThat(assertFailsWith<InvocationTargetException> { noArg.callBy(emptyMap()) }.targetException)
|
||||
.isInstanceOf(UnsupportedOperationException::class.java)
|
||||
.hasMessage("Method has been deleted")
|
||||
assertThat(assertFailsWith<UnsupportedOperationException> { newInstance() })
|
||||
.hasMessage("Method has been deleted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun stubOverloadedIntConstructor() = checkClassWithIntParameter(
|
||||
"net.corda.gradle.HasOverloadedIntConstructorToStub",
|
||||
COUNT_OVERLOADED
|
||||
)
|
||||
|
||||
@Test
|
||||
fun stubMultipleIntConstructor() = checkClassWithIntParameter(
|
||||
"net.corda.gradle.HasMultipleIntConstructorsToStub",
|
||||
COUNT_MULTIPLE
|
||||
)
|
||||
|
||||
private fun checkClassWithIntParameter(intClass: String, constructorCount: Int) {
|
||||
val intConstructor = isConstructor(intClass, Int::class)
|
||||
|
||||
classLoaderFor(testProject.sourceJar).use { cl ->
|
||||
cl.load<HasInt>(intClass).apply {
|
||||
getDeclaredConstructor(Int::class.java).newInstance(NUMBER).also {
|
||||
assertEquals(NUMBER, it.intData())
|
||||
}
|
||||
kotlin.constructors.apply {
|
||||
assertThat("<init>(I) not found", this, hasItem(intConstructor))
|
||||
assertEquals(constructorCount, this.size)
|
||||
}
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
assertThat(primary.call(NUMBER).intData()).isEqualTo(NUMBER)
|
||||
|
||||
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||
assertThat(noArg.callBy(emptyMap()).intData()).isEqualTo(0)
|
||||
assertThat(newInstance().intData()).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
|
||||
classLoaderFor(testProject.filteredJar).use { cl ->
|
||||
cl.load<HasInt>(intClass).apply {
|
||||
getDeclaredConstructor(Int::class.java).newInstance(NUMBER).also {
|
||||
assertEquals(NUMBER, it.intData())
|
||||
}
|
||||
kotlin.constructors.apply {
|
||||
assertThat("<init>(I) not found", this, hasItem(intConstructor))
|
||||
assertEquals(constructorCount, this.size)
|
||||
}
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
assertThat(primary.call(NUMBER).intData()).isEqualTo(NUMBER)
|
||||
|
||||
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||
assertThat(assertFailsWith<InvocationTargetException> { noArg.callBy(emptyMap()) }.targetException)
|
||||
.isInstanceOf(UnsupportedOperationException::class.java)
|
||||
.hasMessage("Method has been deleted")
|
||||
assertThat(assertFailsWith<UnsupportedOperationException> { newInstance() })
|
||||
.hasMessage("Method has been deleted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun stubOverloadedStringConstructor() = checkClassWithStringParameter(
|
||||
"net.corda.gradle.HasOverloadedStringConstructorToStub",
|
||||
COUNT_OVERLOADED
|
||||
)
|
||||
|
||||
@Test
|
||||
fun stubMultipleStringConstructor() = checkClassWithStringParameter(
|
||||
"net.corda.gradle.HasMultipleStringConstructorsToStub",
|
||||
COUNT_MULTIPLE
|
||||
)
|
||||
|
||||
private fun checkClassWithStringParameter(stringClass: String, constructorCount: Int) {
|
||||
val stringConstructor = isConstructor(stringClass, String::class)
|
||||
|
||||
classLoaderFor(testProject.sourceJar).use { cl ->
|
||||
cl.load<HasString>(stringClass).apply {
|
||||
getDeclaredConstructor(String::class.java).newInstance(MESSAGE).also {
|
||||
assertEquals(MESSAGE, it.stringData())
|
||||
}
|
||||
kotlin.constructors.apply {
|
||||
assertThat("<init>(String) not found", this, hasItem(stringConstructor))
|
||||
assertEquals(constructorCount, this.size)
|
||||
}
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
assertThat(primary.call(MESSAGE).stringData()).isEqualTo(MESSAGE)
|
||||
|
||||
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||
assertThat(noArg.callBy(emptyMap()).stringData()).isEqualTo(DEFAULT_MESSAGE)
|
||||
assertThat(newInstance().stringData()).isEqualTo(DEFAULT_MESSAGE)
|
||||
}
|
||||
}
|
||||
|
||||
classLoaderFor(testProject.filteredJar).use { cl ->
|
||||
cl.load<HasString>(stringClass).apply {
|
||||
getDeclaredConstructor(String::class.java).newInstance(MESSAGE).also {
|
||||
assertEquals(MESSAGE, it.stringData())
|
||||
}
|
||||
kotlin.constructors.apply {
|
||||
assertThat("<init>(String) not found", this, hasItem(stringConstructor))
|
||||
assertEquals(constructorCount, this.size)
|
||||
}
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
assertThat(primary.call(MESSAGE).stringData()).isEqualTo(MESSAGE)
|
||||
|
||||
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||
assertThat(assertFailsWith<InvocationTargetException> { noArg.callBy(emptyMap()) }.targetException)
|
||||
.isInstanceOf(UnsupportedOperationException::class.java)
|
||||
.hasMessage("Method has been deleted")
|
||||
assertThat(assertFailsWith<UnsupportedOperationException> { newInstance() })
|
||||
.hasMessage("Method has been deleted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun stubOverloadedComplexConstructor() {
|
||||
val complexConstructor = isConstructor(COMPLEX_CONSTRUCTOR_CLASS, Int::class, String::class)
|
||||
|
||||
classLoaderFor(testProject.sourceJar).use { cl ->
|
||||
cl.load<Any>(COMPLEX_CONSTRUCTOR_CLASS).apply {
|
||||
kotlin.constructors.apply {
|
||||
assertThat("<init>(Int,String) not found", this, hasItem(complexConstructor))
|
||||
assertEquals(1, this.size)
|
||||
}
|
||||
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
primary.call(NUMBER, MESSAGE).also { complex ->
|
||||
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||
assertThat((complex as HasInt).intData()).isEqualTo(NUMBER)
|
||||
}
|
||||
|
||||
primary.callBy(mapOf(primary.parameters[1] to MESSAGE)).also { complex ->
|
||||
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||
assertThat((complex as HasInt).intData()).isEqualTo(0)
|
||||
}
|
||||
getDeclaredConstructor(String::class.java).newInstance(MESSAGE).also { complex ->
|
||||
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||
assertThat((complex as HasInt).intData()).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
classLoaderFor(testProject.filteredJar).use { cl ->
|
||||
cl.load<Any>(COMPLEX_CONSTRUCTOR_CLASS).apply {
|
||||
kotlin.constructors.apply {
|
||||
assertThat("<init>(Int,String) not found", this, hasItem(complexConstructor))
|
||||
assertEquals(1, this.size)
|
||||
}
|
||||
|
||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||
primary.call(NUMBER, MESSAGE).also { complex ->
|
||||
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||
assertThat((complex as HasInt).intData()).isEqualTo(NUMBER)
|
||||
}
|
||||
|
||||
assertThat(assertFailsWith<InvocationTargetException> { primary.callBy(mapOf(primary.parameters[1] to MESSAGE)) }.targetException)
|
||||
.isInstanceOf(kotlin.UnsupportedOperationException::class.java)
|
||||
.hasMessage("Method has been deleted")
|
||||
assertThat(assertFailsWith<InvocationTargetException> { getDeclaredConstructor(String::class.java).newInstance(MESSAGE) }.targetException)
|
||||
.hasMessage("Method has been deleted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -14,6 +14,8 @@ import java.nio.file.Paths
|
||||
import java.util.stream.Collectors.*
|
||||
import java.util.zip.ZipFile
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KParameter
|
||||
|
||||
const val DEFAULT_MESSAGE = "<default-value>"
|
||||
const val MESSAGE = "Goodbye, Cruel World!"
|
||||
@ -65,6 +67,9 @@ fun arrayOfJunk(size: Int) = ByteArray(size).apply {
|
||||
}
|
||||
}
|
||||
|
||||
val <T : Any> KClass<T>.noArgConstructor: KFunction<T>?
|
||||
get() = constructors.firstOrNull { it.parameters.all(KParameter::isOptional) }
|
||||
|
||||
@Throws(MalformedURLException::class)
|
||||
fun classLoaderFor(jar: Path) = URLClassLoader(arrayOf(jar.toUri().toURL()), classLoader)
|
||||
|
||||
|
@ -1,34 +0,0 @@
|
||||
@file:Suppress("UNUSED")
|
||||
package net.corda.gradle
|
||||
|
||||
import net.corda.gradle.jarfilter.DeleteMe
|
||||
import net.corda.gradle.unwanted.*
|
||||
|
||||
private const val DEFAULT_MESSAGE = "<default-value>"
|
||||
|
||||
class HasOverloadedStringConstructor @JvmOverloads @DeleteMe constructor(private val message: String = DEFAULT_MESSAGE) : HasString {
|
||||
override fun stringData(): String = message
|
||||
}
|
||||
|
||||
class HasOverloadedLongConstructor @JvmOverloads @DeleteMe constructor(private val data: Long = 0) : HasLong {
|
||||
override fun longData(): Long = data
|
||||
}
|
||||
|
||||
class HasOverloadedIntConstructor @JvmOverloads @DeleteMe constructor(private val data: Int = 0) : HasInt {
|
||||
override fun intData(): Int = data
|
||||
}
|
||||
|
||||
class HasMultipleStringConstructors(private val message: String) : HasString {
|
||||
@DeleteMe constructor() : this(DEFAULT_MESSAGE)
|
||||
override fun stringData(): String = message
|
||||
}
|
||||
|
||||
class HasMultipleLongConstructors(private val data: Long) : HasLong {
|
||||
@DeleteMe constructor() : this(0)
|
||||
override fun longData(): Long = data
|
||||
}
|
||||
|
||||
class HasMultipleIntConstructors(private val data: Int) : HasInt {
|
||||
@DeleteMe constructor() : this(0)
|
||||
override fun intData(): Int = data
|
||||
}
|
@ -8,7 +8,7 @@ sourceSets {
|
||||
main {
|
||||
kotlin {
|
||||
srcDir files(
|
||||
'../resources/test/sanitise-constructor/kotlin',
|
||||
'../resources/test/sanitise-delete-constructor/kotlin',
|
||||
'../resources/test/annotations/kotlin'
|
||||
)
|
||||
}
|
||||
@ -21,7 +21,7 @@ dependencies {
|
||||
}
|
||||
|
||||
jar {
|
||||
baseName = 'sanitise-constructor'
|
||||
baseName = 'sanitise-delete-constructor'
|
||||
}
|
||||
|
||||
import net.corda.gradle.jarfilter.JarFilterTask
|
@ -0,0 +1,48 @@
|
||||
@file:JvmName("HasOverloadedConstructorsToDelete")
|
||||
@file:Suppress("UNUSED")
|
||||
package net.corda.gradle
|
||||
|
||||
import net.corda.gradle.jarfilter.DeleteMe
|
||||
import net.corda.gradle.unwanted.*
|
||||
|
||||
private const val DEFAULT_MESSAGE = "<default-value>"
|
||||
|
||||
class HasOverloadedStringConstructorToDelete @JvmOverloads @DeleteMe constructor(private val message: String = DEFAULT_MESSAGE) : HasString {
|
||||
override fun stringData(): String = message
|
||||
}
|
||||
|
||||
class HasOverloadedLongConstructorToDelete @JvmOverloads @DeleteMe constructor(private val data: Long = 0) : HasLong {
|
||||
override fun longData(): Long = data
|
||||
}
|
||||
|
||||
class HasOverloadedIntConstructorToDelete @JvmOverloads @DeleteMe constructor(private val data: Int = 0) : HasInt {
|
||||
override fun intData(): Int = data
|
||||
}
|
||||
|
||||
/**
|
||||
* This case is complex because:
|
||||
* - The primary constructor has two parameters.
|
||||
* - The first constructor parameter has a default value.
|
||||
* - The second constructor parameter is mandatory.
|
||||
*/
|
||||
class HasOverloadedComplexConstructorToDelete @JvmOverloads @DeleteMe constructor(private val data: Int = 0, private val message: String)
|
||||
: HasInt, HasString
|
||||
{
|
||||
override fun stringData(): String = message
|
||||
override fun intData(): Int = data
|
||||
}
|
||||
|
||||
class HasMultipleStringConstructorsToDelete(private val message: String) : HasString {
|
||||
@DeleteMe constructor() : this(DEFAULT_MESSAGE)
|
||||
override fun stringData(): String = message
|
||||
}
|
||||
|
||||
class HasMultipleLongConstructorsToDelete(private val data: Long) : HasLong {
|
||||
@DeleteMe constructor() : this(0)
|
||||
override fun longData(): Long = data
|
||||
}
|
||||
|
||||
class HasMultipleIntConstructorsToDelete(private val data: Int) : HasInt {
|
||||
@DeleteMe constructor() : this(0)
|
||||
override fun intData(): Int = data
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '$kotlin_version'
|
||||
id 'net.corda.plugins.jar-filter'
|
||||
}
|
||||
apply from: 'repositories.gradle'
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
kotlin {
|
||||
srcDir files(
|
||||
'../resources/test/sanitise-stub-constructor/kotlin',
|
||||
'../resources/test/annotations/kotlin'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||
compileOnly files('../../unwanteds/build/libs/unwanteds.jar')
|
||||
}
|
||||
|
||||
jar {
|
||||
baseName = 'sanitise-stub-constructor'
|
||||
}
|
||||
|
||||
import net.corda.gradle.jarfilter.JarFilterTask
|
||||
task jarFilter(type: JarFilterTask) {
|
||||
jars jar
|
||||
annotations {
|
||||
forStub = ["net.corda.gradle.jarfilter.StubMeOut"]
|
||||
forSanitise = ["net.corda.gradle.jarfilter.StubMeOut"]
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
@file:JvmName("HasOverloadedConstructorsToStub")
|
||||
@file:Suppress("UNUSED")
|
||||
package net.corda.gradle
|
||||
|
||||
import net.corda.gradle.jarfilter.StubMeOut
|
||||
import net.corda.gradle.unwanted.*
|
||||
|
||||
private const val DEFAULT_MESSAGE = "<default-value>"
|
||||
|
||||
class HasOverloadedStringConstructorToStub @JvmOverloads @StubMeOut constructor(private val message: String = DEFAULT_MESSAGE) : HasString {
|
||||
override fun stringData(): String = message
|
||||
}
|
||||
|
||||
class HasOverloadedLongConstructorToStub @JvmOverloads @StubMeOut constructor(private val data: Long = 0) : HasLong {
|
||||
override fun longData(): Long = data
|
||||
}
|
||||
|
||||
class HasOverloadedIntConstructorToStub @JvmOverloads @StubMeOut constructor(private val data: Int = 0) : HasInt {
|
||||
override fun intData(): Int = data
|
||||
}
|
||||
|
||||
/**
|
||||
* This case is complex because:
|
||||
* - The primary constructor has two parameters.
|
||||
* - The first constructor parameter has a default value.
|
||||
* - The second constructor parameter is mandatory.
|
||||
*/
|
||||
class HasOverloadedComplexConstructorToStub @JvmOverloads @StubMeOut constructor(private val data: Int = 0, private val message: String)
|
||||
: HasInt, HasString
|
||||
{
|
||||
override fun stringData(): String = message
|
||||
override fun intData(): Int = data
|
||||
}
|
||||
|
||||
class HasMultipleStringConstructorsToStub(private val message: String) : HasString {
|
||||
@StubMeOut constructor() : this(DEFAULT_MESSAGE)
|
||||
override fun stringData(): String = message
|
||||
}
|
||||
|
||||
class HasMultipleLongConstructorsToStub(private val data: Long) : HasLong {
|
||||
@StubMeOut constructor() : this(0)
|
||||
override fun longData(): Long = data
|
||||
}
|
||||
|
||||
class HasMultipleIntConstructorsToStub(private val data: Int) : HasInt {
|
||||
@StubMeOut constructor() : this(0)
|
||||
override fun intData(): Int = data
|
||||
}
|
@ -172,6 +172,7 @@ task checkDeterminism(type: ProGuardTask, dependsOn: jdkTask) {
|
||||
defaultTasks "determinise"
|
||||
determinise.finalizedBy metafix
|
||||
metafix.finalizedBy checkDeterminism
|
||||
assemble.dependsOn checkDeterminism
|
||||
|
||||
def deterministicJar = metafix.outputs.files.singleFile
|
||||
artifacts {
|
||||
|
@ -157,6 +157,7 @@ task checkDeterminism(type: ProGuardTask, dependsOn: jdkTask) {
|
||||
defaultTasks "determinise"
|
||||
determinise.finalizedBy metafix
|
||||
metafix.finalizedBy checkDeterminism
|
||||
assemble.dependsOn checkDeterminism
|
||||
|
||||
def deterministicJar = metafix.outputs.files.singleFile
|
||||
artifacts {
|
||||
|
Loading…
Reference in New Issue
Block a user