From 23b070c7d02a8da44bf04c2f9ba2b485a44ad967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Wed, 8 May 2013 05:36:28 +0200 Subject: [PATCH] Alternative @SidedProxy setter for Scala. --- .../cpw/mods/fml/common/ILanguageAdapter.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/common/cpw/mods/fml/common/ILanguageAdapter.java b/common/cpw/mods/fml/common/ILanguageAdapter.java index 6aae70b66a..24b00e40f1 100644 --- a/common/cpw/mods/fml/common/ILanguageAdapter.java +++ b/common/cpw/mods/fml/common/ILanguageAdapter.java @@ -1,6 +1,8 @@ package cpw.mods.fml.common; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; public interface ILanguageAdapter { public Object getNewInstance(FMLModContainer container, Class objectClass, ClassLoader classLoader) throws Exception; @@ -24,6 +26,51 @@ public interface ILanguageAdapter { @Override public void setProxy(Field target, Class proxyTarget, Object proxy) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + try + { + // Check if the target class is a singleton, if so its + // implementation's class name is the actual classname + // postfixed with a $. + final Class targetSingleton = Class.forName(proxyTarget.getName() + "$", true, proxyTarget.getClassLoader()); + + // Get the instance via the MODULE$ field which is + // automatically generated by the scala compiler for + // singletons. + final Object targetInstance = targetSingleton.getField("MODULE$").get(null); + + // Find setter function. We do it this way because we can't + // necessarily use proxyTarget.getMethod(proxy.getClass()), as + // it might be a subclass and not the exact parameter type. + // All fields are private in scala, wrapped by a getter and + // setter named and _$eq. To those + // familiar with scala.reflect.BeanProperty: these will always + // be there, set and get will always + // only be generated *additionally*. + final String setterName = target.getName() + "_$eq"; + for (Method setter : proxyTarget.getMethods()) + { + Class[] setterParameters = setter.getParameterTypes(); + if (setterName.equals(setter.getName()) && + // Some more validation. + setterParameters.length == 1 && + setterParameters[0].isAssignableFrom(proxy.getClass())) + { + // Here goes nothing... if this invocation fails we'll + // just fall back to the original functionality. + setter.invoke(targetInstance, proxy); + return; + } + } + } + catch (ClassNotFoundException e) + { + FMLLog.fine("This scala class is not a singleton (scala object), falling back to old proxy setter."); + } + catch (InvocationTargetException e) + { + FMLLog.fine("There's a problem with this proxy's setter, falling back to old proxy setter."); + } + Field field = proxyTarget.getField("INSTANCE"); Object scalaObject = field.get(null); target.set(scalaObject, proxy);