From ca9eb00cf6cfcc259a2c312ade69d201855c5ea8 Mon Sep 17 00:00:00 2001 From: danielperano <51095634+danielperano@users.noreply.github.com> Date: Wed, 17 Apr 2024 11:48:44 -0700 Subject: [PATCH] Added linkage sort order to predictably handle ambiguous linkage cases introduced by automatic proxying. --- .../chipmunk/vm/invoke/ChipmunkLinker.java | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/Lang/src/main/java/chipmunk/vm/invoke/ChipmunkLinker.java b/Lang/src/main/java/chipmunk/vm/invoke/ChipmunkLinker.java index 13f1d3f..6790eb0 100644 --- a/Lang/src/main/java/chipmunk/vm/invoke/ChipmunkLinker.java +++ b/Lang/src/main/java/chipmunk/vm/invoke/ChipmunkLinker.java @@ -219,6 +219,42 @@ public GuardedInvocation resolveCallTarget(MethodHandles.Lookup lookup, Object r return null; } + public static void linkOrder(Method[] methods){ + Arrays.sort(methods, (m1, m2) -> { + // Favor shorter parameter lists first + if(m1.getParameterCount() < m2.getParameterCount()){ + return -1; + } + + // Favor simple types over interfaces (which will be proxied to). This + // ensures that interface linkage only happens if a more appropriate linkage + // is not found first. This is important because without type information, + // interface receiver types will match with any parameter, resulting in some + // cases where interface proxying happens when a different method is available + // with an identical type match. Worse, these scenarios are unpredictable since + // the order of methods returned by Object.getMethods() is not specified. + if(m1.getParameterCount() == m2.getParameterCount()){ + var p1Types = m1.getParameterTypes(); + var p2Types = m2.getParameterTypes(); + int m1Weight = 0; + for(int i = 0; i < p1Types.length; i++){ + var p1IsInterface = p1Types[i].isInterface(); + var p2IsInterface = p2Types[i].isInterface(); + + if(p1IsInterface && !p2IsInterface){ + m1Weight++; + }else if(!p1IsInterface && p2IsInterface){ + m1Weight--; + } + } + return m1Weight; + } + + // At this point we know m2 has a shorter parameter list + return 1; + }); + } + public MethodHandle getMethod(Object receiver, Class expectedReturnType, String methodName, Object[] params, Class[] pTypes, boolean enforceLinkagePolicy) throws IllegalAccessException, NoSuchMethodException { Class receiverType; @@ -230,7 +266,9 @@ public MethodHandle getMethod(Object receiver, Class expectedReturnType, Stri receiverType = receiver.getClass(); } - for (Method m : receiverType.getMethods()) { + var methods = receiverType.getMethods(); + linkOrder(methods); + for (Method m : methods) { Class[] candidatePTypes = m.getParameterTypes(); if (candidatePTypes.length != pTypes.length - 1 && !m.isVarArgs()) {