Skip to content

Commit

Permalink
Resolves issue 395. Declared exceptions are part of the ABI.
Browse files Browse the repository at this point in the history
  • Loading branch information
autonomousapps committed Aug 29, 2021
1 parent 963c309 commit d654b71
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.autonomousapps.jvm

import com.autonomousapps.jvm.projects.AbiExceptionsProject

import static com.autonomousapps.utils.Runner.build
import static com.google.common.truth.Truth.assertThat

final class AbiExceptionsSpec extends AbstractJvmSpec {

def "declared exceptions are part of the abi (#gradleVersion)"() {
given:
def project = new AbiExceptionsProject()
gradleProject = project.gradleProject
when:
build(gradleVersion, gradleProject.rootDir, ':buildHealth')
then:
assertThat(project.actualBuildHealth()).containsExactlyElementsIn(project.expectedBuildHealth)
where:
gradleVersion << gradleVersions()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.autonomousapps.jvm.projects

import com.autonomousapps.AbstractProject
import com.autonomousapps.advice.ComprehensiveAdvice
import com.autonomousapps.kit.GradleProject
import com.autonomousapps.kit.Plugin
import com.autonomousapps.kit.Source
import com.autonomousapps.kit.SourceType

import static com.autonomousapps.AdviceHelper.actualBuildHealth
import static com.autonomousapps.AdviceHelper.emptyBuildHealthFor
import static com.autonomousapps.kit.Dependency.project

final class AbiExceptionsProject extends AbstractProject {

final GradleProject gradleProject

AbiExceptionsProject() {
this.gradleProject = build()
}

private GradleProject build() {
def builder = newGradleProjectBuilder()
builder.withSubproject('proj') { s ->
s.sources = libSources
s.withBuildScript { bs ->
bs.plugins = [Plugin.javaLibraryPlugin]
bs.dependencies = [project('api', ':exceptions')]
}
}
builder.withSubproject('exceptions') { s ->
s.sources = exceptionsSources
s.withBuildScript { bs ->
bs.plugins = [Plugin.javaLibraryPlugin]
}
}

def project = builder.build()
project.writer().write()
return project
}

private libSources = [
new Source(
SourceType.JAVA, "Sup", "com/example",
"""\
package com.example;
public interface Sup {}
""".stripIndent()
),
new Source(
SourceType.JAVA, "Main", "com/example",
"""\
package com.example;
import com.example.exception.FancyException;
public class Main implements Sup {
public String magic() throws FancyException {
return "42";
}
}
""".stripIndent()
)
]

private exceptionsSources = [
new Source(
SourceType.JAVA, "FancyException", "com/example/exception",
"""\
package com.example.exception;
public class FancyException extends RuntimeException {}
""".stripIndent()
)
]

List<ComprehensiveAdvice> actualBuildHealth() {
return actualBuildHealth(gradleProject)
}

final List<ComprehensiveAdvice> expectedBuildHealth = emptyBuildHealthFor(
':proj', ':exceptions', ':'
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ fun getBinaryAPI(classStreams: Sequence<InputStream>, visibilityFilter: (String)
parameterAnnotations = parameterAnnotations,
typeAnnotations = typeAnnotations,
isPublishedApi = isPublishedApi(),
access = AccessFlags(access)
access = AccessFlags(access),
// nb: MethodNode.exceptions is NOT expressed as a type descriptor, rather as a path.
// e.g., not `Lcom/example/Foo;`, but just `com/example/Foo`
exceptions = exceptions
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,12 @@ private fun List<ClassBinarySignature>.dependencies(
.flatMapToSet { it.typeAnnotations }
.flatMapToSet { DESC_REGEX.findAll(it).allItems() }

val exceptions = classSignature.memberSignatures
.filterIsInstance<MethodBinarySignature>()
.flatMapToSet { it.exceptions } // no need for DESC_REGEX

// return
superTypes + memberTypes + memberGenericTypes + classAnnotations + memberAnnotations + parameterAnnotations + typeAnnotations
superTypes + memberTypes + memberGenericTypes + classAnnotations + memberAnnotations + parameterAnnotations + typeAnnotations + exceptions
}.mapToSet {
it.replace("/", ".")
}
Expand Down
14 changes: 11 additions & 3 deletions src/main/kotlin/com/autonomousapps/internal/kotlin/asmUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,21 @@ data class MethodBinarySignature(
override val jvmMember: JvmMethodSignature,
override val genericTypes: Set<String>,
override val annotations: Set<String>,
override val isPublishedApi: Boolean,
override val access: AccessFlags,
val parameterAnnotations: List<String>,
val typeAnnotations: List<String>,
override val isPublishedApi: Boolean,
override val access: AccessFlags
/** Not expressed as type descriptors, instead just `com/example/Foo`. */
val exceptions: List<String>
) : MemberBinarySignature {

override val signature: String
get() = "${access.getModifierString()} fun $name $desc"
get() = "${access.getModifierString()} fun $name $desc$throws"

private val throws = if (exceptions.isEmpty()) "" else exceptions.joinToString(prefix = " throws ") {
// The ABI dump uses descriptor strings
"L$it;"
}

override fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?) =
super.isEffectivelyPublic(classAccess, classVisibility)
Expand Down

0 comments on commit d654b71

Please sign in to comment.