package org.ultramine.mods.scripting; import groovy.lang.MetaClass; import groovy.lang.MetaClassImpl; import groovy.lang.MetaClassRegistry; import groovy.lang.MetaMethod; import groovy.lang.MetaProperty; import org.codehaus.groovy.reflection.CachedClass; import org.codehaus.groovy.reflection.CachedField; import org.codehaus.groovy.reflection.CachedMethod; import org.codehaus.groovy.runtime.GeneratedClosure; import org.codehaus.groovy.runtime.MetaClassHelper; import org.codehaus.groovy.runtime.callsite.CallSite; import org.codehaus.groovy.runtime.callsite.PojoMetaClassSite; import org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite; import org.codehaus.groovy.runtime.callsite.StaticMetaClassSite; import org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite; import org.codehaus.groovy.runtime.metaclass.MetaMethodIndex; import org.codehaus.groovy.util.ComplexKeyHashMap; import org.codehaus.groovy.util.SingleKeyHashMap; import org.ultramine.mods.scripting.deobf.DeobfManager; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.LinkedList; public class CustomMetaClassCreationHandle extends MetaClassRegistry.MetaClassCreationHandle { private static final Field F_classPropertyIndex; static { try { F_classPropertyIndex = MetaClassImpl.class.getDeclaredField("classPropertyIndex"); F_classPropertyIndex.setAccessible(true); } catch(Exception e) { throw new RuntimeException(e); } } private static MetaClassImpl.Index getClassPropertyIndex(MetaClassImpl mc) { try { return (MetaClassImpl.Index)F_classPropertyIndex.get(mc); } catch(IllegalAccessException e) { throw new RuntimeException(e); } } private final boolean obfenv; public CustomMetaClassCreationHandle(boolean obfenv) { this.obfenv = obfenv; } @Override protected MetaClass createNormalMetaClass(Class theClass, MetaClassRegistry registry) { if (GeneratedClosure.class.isAssignableFrom(theClass)) { return super.createNormalMetaClass(theClass, registry); } else { MetaClassImpl mc = new MetaClassImpl(registry, theClass) { MetaClassImpl.Index classPropertyIndex = getClassPropertyIndex(this); @Override public MetaMethod getMethodWithCaching(Class sender, String methodName, Object[] arguments, boolean isCallToSuper) { MetaMethod mm = super.getMethodWithCaching(sender, methodName, arguments, isCallToSuper); if(mm == null && obfenv) { String newName = DeobfManager.getInstance().mapMethodName(getTheClass(), methodName); if(newName != null) mm = super.getMethodWithCaching(sender, newName, arguments, isCallToSuper); } return mm; } @Override public CallSite createPojoCallSite(CallSite site, Object receiver, Object[] args) { CallSite cs = super.createPojoCallSite(site, receiver, args); if(cs instanceof PojoMetaClassSite && obfenv) { String newName = DeobfManager.getInstance().mapMethodName(getTheClass(), site.getName()); if(newName != null) { Class[] params = MetaClassHelper.convertToTypeArray(args); CachedMethod cm = (CachedMethod)getMetaMethod(newName, params); if(cm != null) return PojoMetaMethodSite.createCachedMethodSite(site, this, cm, params, args); } } return cs; } @Override public CallSite createStaticSite(CallSite site, Object[] args) { CallSite cs = super.createStaticSite(site, args); if(cs instanceof StaticMetaClassSite && obfenv) { String newName = DeobfManager.getInstance().mapMethodName(getTheClass(), site.getName()); if(newName != null) { Class[] params = MetaClassHelper.convertToTypeArray(args); CachedMethod cm = (CachedMethod)getMetaMethod(newName, params); if(cm != null) return StaticMetaMethodSite.createStaticMetaMethodSite(site, this, cm, params, args); } } return cs; } @Override public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) { String newName = obfenv ? DeobfManager.getInstance().mapFieldName(object instanceof Class ? (Class) object : object.getClass(), name) : null; return super.getProperty(sender, object, newName != null ? newName : name, useSuper, fromInsideClass); } @Override public MetaProperty getEffectiveGetMetaProperty(Class sender, Object object, String name, boolean useSuper) { String newName = obfenv ? DeobfManager.getInstance().mapFieldName(object instanceof Class ? (Class)object : object.getClass(), name) : null; return super.getEffectiveGetMetaProperty(sender, object, newName != null ? newName : name, useSuper); } @Override public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) { String newName = obfenv ? DeobfManager.getInstance().mapFieldName(object instanceof Class ? (Class)object : object.getClass(), name) : null; super.setProperty(sender, object, newName != null ? newName : name, newValue, useSuper, fromInsideClass); } /* Allow access super private methods and fields */ @Override public synchronized void initialize() { if (!isInitialized()) { super.initialize(); LinkedList<CachedClass> superc = getSuperClasses(); MetaMethodIndex.Header header = metaMethodIndex.getHeader(theClass); for (CachedClass c : superc) for(CachedMethod metaMethod : c.getMethods()) if(metaMethod.isPrivate()) addMetaMethodToIndex(metaMethod, header); inheritPrivateFields(superc); } } private void inheritPrivateFields(LinkedList<CachedClass> superClasses) { SingleKeyHashMap last = null; for(CachedClass klass : superClasses) { SingleKeyHashMap propertyIndex = classPropertyIndex.getNotNull(klass); if(last != null) { copyPrivateFields(last, propertyIndex); } last = propertyIndex; } } private void copyPrivateFields(SingleKeyHashMap from, SingleKeyHashMap to) { for(ComplexKeyHashMap.EntryIterator iter = from.getEntrySetIterator(); iter.hasNext();) { SingleKeyHashMap.Entry entry = (SingleKeyHashMap.Entry) iter.next(); MetaProperty mfp = (MetaProperty) entry.getValue(); if(mfp instanceof CachedField && Modifier.isPrivate(mfp.getModifiers())) to.put(entry.getKey(), mfp); } } }; return mc; } } }