Skip to content

Commit

Permalink
Add validation for method invocation types
Browse files Browse the repository at this point in the history
Report when

* invokevirtual calls an interface method
* invokeinterface calls a class method
* non-constructor invokespecial calls a method on a class outside of
caller's hierarchy
  • Loading branch information
ogolberg authored May 10, 2024
1 parent dbe8244 commit 398d227
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 0 deletions.
7 changes: 7 additions & 0 deletions core/src/main/kotlin/com/toasttab/expediter/Expediter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import com.toasttab.expediter.types.members
import protokt.v1.toasttab.expediter.v1.AccessDeclaration
import protokt.v1.toasttab.expediter.v1.MemberDescriptor
import protokt.v1.toasttab.expediter.v1.TypeExtensibility
import protokt.v1.toasttab.expediter.v1.TypeFlavor

class Expediter(
private val ignore: Ignore,
Expand Down Expand Up @@ -167,6 +168,12 @@ class Expediter(
Issue.AccessStaticMemberNonStatically(type.name, resolvedAccess)
} else if (member.member.declaration == AccessDeclaration.INSTANCE && access.accessType.isStatic()) {
Issue.AccessInstanceMemberStatically(type.name, resolvedAccess)
} else if (access.accessType == MethodAccessType.INTERFACE && chain.type.descriptor.flavor == TypeFlavor.CLASS) {
Issue.InterfaceCallToClass(type.name, resolvedAccess)
} else if (access.accessType == MethodAccessType.VIRTUAL && chain.type.descriptor.flavor == TypeFlavor.INTERFACE) {
Issue.VirtualCallToInterface(type.name, resolvedAccess)
} else if (access.accessType == MethodAccessType.SPECIAL && !access.ref.isConstructor() && hierarchy is ResolvedTypeHierarchy.CompleteTypeHierarchy && !hierarchy.allTypes.contains(chain.type)) {
Issue.SpecialCallOutOfHierarchy(type.name, resolvedAccess)
} else if (!AccessCheck.allowedAccess(hierarchy, chain, member.member, member.declaringType)) {
Issue.AccessInaccessibleMember(type.name, resolvedAccess)
} else {
Expand Down
21 changes: 21 additions & 0 deletions model/src/main/kotlin/com/toasttab/expediter/issue/Issue.kt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,27 @@ sealed interface Issue {
override fun toString() = "$caller accesses inaccessible $member"
}

@Serializable
@SerialName("virtual-call-to-interface")
data class VirtualCallToInterface(override val caller: String, override val member: MemberAccess) : Issue, WithMemberAccess {
override val target: String get() = member.targetType
override fun toString() = "$caller accesses interface method $member virtually"
}

@Serializable
@SerialName("interface-call-to-class")
data class InterfaceCallToClass(override val caller: String, override val member: MemberAccess) : Issue, WithMemberAccess {
override val target: String get() = member.targetType
override fun toString() = "$caller accesses class method $member interfacely"
}

@Serializable
@SerialName("special-call-out-of-hierarchy")
data class SpecialCallOutOfHierarchy(override val caller: String, override val member: MemberAccess) : Issue, WithMemberAccess {
override val target: String get() = member.targetType
override fun toString() = "$caller makes a special call to non-contructor $member which is not in its hierarchy"
}

@Serializable
data class UnknownIssue internal constructor(
val type: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.toasttab.expediter.test;

public class WasClass {
public void foo() { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.toasttab.expediter.test;

public interface WasInterface {
void foo();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.toasttab.expediter.test;

public interface WasClass {
void foo();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.toasttab.expediter.test;

public class WasInterface {
void foo() { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,26 @@ class ExpediterIntegrationTest {
)
),

Issue.VirtualCallToInterface(
"com/toasttab/expediter/test/caller/Caller",
MemberAccess.MethodAccess(
"com/toasttab/expediter/test/WasClass",
"com/toasttab/expediter/test/WasClass",
MemberSymbolicReference("foo", "()V"),
MethodAccessType.VIRTUAL
)
),

Issue.InterfaceCallToClass(
"com/toasttab/expediter/test/caller/Caller",
MemberAccess.MethodAccess(
"com/toasttab/expediter/test/WasInterface",
"com/toasttab/expediter/test/WasInterface",
MemberSymbolicReference("foo", "()V"),
MethodAccessType.INTERFACE
)
),

Issue.MissingApplicationSuperType(
"com/toasttab/expediter/test/Foo",
setOf("com/toasttab/expediter/test/BaseFoo")
Expand Down Expand Up @@ -225,6 +245,8 @@ class ExpediterIntegrationTest {
"com/toasttab/expediter/test/caller/Caller accesses inaccessible com/toasttab/expediter/test/Bar.bar(F)V (via com/toasttab/expediter/test/Baz)",
"com/toasttab/expediter/test/caller/Caller accesses inaccessible com/toasttab/expediter/test/Bar.bar(I)V",
"com/toasttab/expediter/test/caller/Caller accesses inaccessible com/toasttab/expediter/test/Bar.bar(J)V",
"com/toasttab/expediter/test/caller/Caller accesses interface method com/toasttab/expediter/test/WasClass.foo()V virtually",
"com/toasttab/expediter/test/caller/Caller accesses class method com/toasttab/expediter/test/WasInterface.foo()V interfacely",
"com/toasttab/expediter/test/Foo extends missing type com/toasttab/expediter/test/BaseFoo",
"duplicate class com/toasttab/expediter/test/Dupe in [testFixtures, lib2-test-fixtures.jar]"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import com.toasttab.expediter.test.Lambda;
import com.toasttab.expediter.test.ParamParam;
import com.toasttab.expediter.test.Var;
import com.toasttab.expediter.test.WasClass;
import com.toasttab.expediter.test.WasInterface;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
Expand Down Expand Up @@ -112,4 +114,12 @@ boolean missingTypeInstanceof(Object o) {
void superMethodMoved() {
super.supersuper();
}

void interfaceToClass(WasInterface o) {
o.foo();
}

void virtualToInterface(WasClass o) {
o.foo();
}
}

0 comments on commit 398d227

Please sign in to comment.