Skip to content

Commit

Permalink
Exclude polymorphic methods
Browse files Browse the repository at this point in the history
  • Loading branch information
ogolberg authored Dec 24, 2023
1 parent f4858cc commit 7e18653
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 2 deletions.
9 changes: 8 additions & 1 deletion core/src/main/kotlin/com/toasttab/expediter/Expediter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.toasttab.expediter.types.MemberType
import com.toasttab.expediter.types.MethodAccessType
import com.toasttab.expediter.types.OptionalResolvedTypeHierarchy
import com.toasttab.expediter.types.PlatformType
import com.toasttab.expediter.types.PolymorphicMethods
import com.toasttab.expediter.types.ResolvedTypeHierarchy
import com.toasttab.expediter.types.Type
import com.toasttab.expediter.types.members
Expand Down Expand Up @@ -151,7 +152,13 @@ class Expediter(
val member = chain.resolveMember(access)

if (member == null) {
Issue.MissingMember(type.name, access)
if (access.accessType == MethodAccessType.VIRTUAL && PolymorphicMethods.contains(access.targetType, access.ref.name)) {
// if invoking a polymorphic method, assume the invocation is valid
// because it's validated by the java compiler
null
} else {
Issue.MissingMember(type.name, access)
}
} else {
val resolvedAccess = access.withDeclaringType(member.declaringType.name)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.toasttab.expediter.types

/**
* This object enumerates signature-polymorphic methods, see the javadoc for MethodHandle.
*/
object PolymorphicMethods {
private val methods = mapOf(
"java/lang/invoke/MethodHandle" to setOf(
"invoke",
"invokeExact"
),

"java/lang/invoke/VarHandle" to setOf(
"get",
"set",
"getVolatile",
"setVolatile",
"getOpaque",
"setOpaque",
"getAcquire",
"setRelease",
"compareAndSet",
"compareAndExchange",
"compareAndExchangeAcquire",
"compareAndExchangeRelease",
"weakCompareAndSetPlain",
"weakCompareAndSet",
"weakCompareAndSetAcquire",
"weakCompareAndSetRelease",
"getAndSet",
"getAndSetAcquire",
"getAndSetRelease",
"getAndAdd",
"getAndAddAcquire",
"getAndAddRelease",
"getAndBitwiseOr",
"getAndBitwiseOrAcquire",
"getAndBitwiseOrRelease",
"getAndBitwiseAnd",
"getAndBitwiseAndAcquire",
"getAndBitwiseAndRelease",
"getAndBitwiseXor",
"getAndBitwiseXorAcquire",
"getAndBitwiseXorRelease"
)
)

fun contains(className: String, methodName: String): Boolean {
return methods[className]?.contains(methodName) ?: false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.toasttab.expediter.types

import org.junit.jupiter.api.Test
import strikt.api.expectThat
import strikt.assertions.isTrue
import java.lang.invoke.MethodHandle
import java.lang.invoke.VarHandle
import java.lang.reflect.Modifier

class PolymorphicMethodsTest {
@Test
fun `MethodHandle and VarHandle methods annotated with PolymorphicSignature`() {
for (cls in listOf(MethodHandle::class.java, VarHandle::class.java)) {
val name = cls.name.replace('.', '/')

for (method in polymorphicMethodsOf(cls)) {
expectThat(PolymorphicMethods.contains(name, method.name)).isTrue()
}
}
}

private fun polymorphicMethodsOf(cls: Class<*>) = cls.methods.filter { m ->
Modifier.isPublic(m.modifiers) && m.annotations.any {
it.annotationClass.java.name == "java.lang.invoke.MethodHandle\$PolymorphicSignature"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
import com.toasttab.expediter.test.ParamParam;
import com.toasttab.expediter.test.Var;

import java.util.stream.Stream;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;

public final class Caller extends Base {
Foo foo;
Expand Down Expand Up @@ -105,4 +108,20 @@ void missingTypeMethodArg() {
boolean missingTypeInstanceof(Object o) {
return o instanceof ParamParam[];
}

void methodHandle() throws Throwable {
MethodHandle h1 = MethodHandles.publicLookup().findVirtual(String.class, "substring", MethodType.methodType(String.class, int.class, int.class));

h1.invokeExact("xxx", 1, 2);

MethodHandle h2 = MethodHandles.publicLookup().findStatic(String.class, "valueOf", MethodType.methodType(String.class, long.class));

h2.invokeExact(1L);
}

void varHandle() throws Throwable {
VarHandle vh = MethodHandles.publicLookup().findVarHandle(int[].class, "length", int.class);

vh.get(new int[0]);
}
}

0 comments on commit 7e18653

Please sign in to comment.