Newer
Older
ultramine_scripting / src / main / java / org / ultramine / mods / scripting / CustomMetaClassCreationHandle.java
@vlad20012 vlad20012 on 29 Mar 2016 6 KB initial commit
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;
		}
	}
}