/** * OpenAtlasForAndroid Project * The MIT License (MIT) Copyright (OpenAtlasForAndroid) 2015 Bunny Blue,achellies * <p/> * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following conditions: * <p/> * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * <p/> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * @author BunnyBlue **/ package com.openatlas.hack; import com.openatlas.hack.Hack.HackDeclaration.HackAssertionException; import com.openatlas.hack.Interception.InterceptionHandler; import com.openatlas.runtime.DelegateClassLoader; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; public class Hack { private static AssertionFailureHandler sFailureHandler; public interface AssertionFailureHandler { boolean onAssertionFailure(HackAssertionException hackAssertionException); } public static abstract class HackDeclaration { public static class HackAssertionException extends Throwable { private static final long serialVersionUID = 1; private Class<?> mHackedClass; private String mHackedFieldName; private String mHackedMethodName; public HackAssertionException(String cause) { super(cause); } public HackAssertionException(Exception exception) { super(exception); } @Override public String toString() { return getCause() != null ? getClass().getName() + ": " + getCause() : super.toString(); } public Class<?> getHackedClass() { return this.mHackedClass; } public void setHackedClass(Class<?> cls) { this.mHackedClass = cls; } public String getHackedMethodName() { return this.mHackedMethodName; } public void setHackedMethodName(String name) { this.mHackedMethodName = name; } public String getHackedFieldName() { return this.mHackedFieldName; } public void setHackedFieldName(String name) { this.mHackedFieldName = name; } } } public static class HackedClass<C> { protected Class<C> mClass; public HackedField<C, Object> staticField(String name) throws HackAssertionException { return new HackedField(this.mClass, name, Modifier.STATIC); } public HackedField<C, Object> field(String name) throws HackAssertionException { return new HackedField(this.mClass, name, 0); } public HackedMethod staticMethod(String name, Class<?>... parameterTypes) throws HackAssertionException { return new HackedMethod(this.mClass, name, parameterTypes, Modifier.STATIC); } public HackedMethod method(String name, Class<?>... parameterTypes) throws HackAssertionException { return new HackedMethod(this.mClass, name, parameterTypes, 0); } public HackedConstructor constructor(Class<?>... parameterTypes) throws HackAssertionException { return new HackedConstructor(this.mClass, parameterTypes); } public HackedClass(Class<C> cls) { this.mClass = cls; } public Class<C> getmClass() { return this.mClass; } } public static class HackedConstructor { protected Constructor<?> mConstructor; HackedConstructor(Class<?> clazz, Class<?>[] parameterTypes) throws HackAssertionException { if (clazz != null) { try { this.mConstructor = clazz.getDeclaredConstructor(parameterTypes); } catch (Exception e) { HackAssertionException hackAssertionException = new HackAssertionException(e); hackAssertionException.setHackedClass(clazz); Hack.fail(hackAssertionException); } } } public Object getInstance(Object... args) throws IllegalArgumentException { this.mConstructor.setAccessible(true); try { return this.mConstructor.newInstance(args); } catch (Exception e) { e.printStackTrace(); } return null; } } public static class HackedField<C, T> { private final Field mField; public <T2> com.openatlas.hack.Hack.HackedField<C, T2> ofGenericType(Class<?> cls) throws HackAssertionException { if (!(this.mField == null || cls.isAssignableFrom(this.mField.getType()))) { Hack.fail(new HackAssertionException(new ClassCastException( this.mField + " is not of type " + cls))); } return (HackedField<C, T2>) this; } public <T2> com.openatlas.hack.Hack.HackedField<C, T2> ofType( Class<T2> cls) throws HackAssertionException { if (!(this.mField == null || cls.isAssignableFrom(this.mField .getType()))) { Hack.fail(new HackAssertionException(new ClassCastException( this.mField + " is not of type " + cls))); } return (HackedField<C, T2>) this; } public com.openatlas.hack.Hack.HackedField<C, T> ofType( String className) throws HackAssertionException { com.openatlas.hack.Hack.HackedField<C, T> ofType = null; try { ofType = (HackedField<C, T>) ofType(Class.forName(className)); } catch (Exception e) { Hack.fail(new HackAssertionException(e)); } return ofType; } public T get(C object) { try { return (T) this.mField.get(object); } catch (IllegalAccessException e) { e.printStackTrace(); return null; } } public void set(C object, Object value) { try { this.mField.set(object, value); } catch (Throwable e) { e.printStackTrace(); if (value instanceof DelegateClassLoader) { throw new RuntimeException("set DelegateClassLoader fail", e); } } } public void hijack(C c, InterceptionHandler<?> interceptionHandler) { Object obj = get(c); if (obj == null) { throw new IllegalStateException("Cannot hijack null"); } set(c, Interception.proxy(obj, interceptionHandler, obj.getClass() .getInterfaces())); } HackedField(Class<C> cls, String name, int modifier) throws HackAssertionException { Field field = null; if (cls == null) { this.mField = null; return; } try { field = cls.getDeclaredField(name); if (modifier > 0 && (field.getModifiers() & modifier) != modifier) { Hack.fail(new HackAssertionException(field + " does not match modifiers: " + modifier)); } field.setAccessible(true); } catch (Exception e) { HackAssertionException hackAssertionException = new HackAssertionException(e); hackAssertionException.setHackedClass(cls); hackAssertionException.setHackedFieldName(name); Hack.fail(hackAssertionException); } finally { this.mField = field; } } public Field getField() { return this.mField; } } public static class HackedMethod { protected final Method mMethod; /**** * @param receiver the object on which to call this method (or null for static methods) * @param args the arguments to the method * @return the result * @throws IllegalArgumentException * @throws InvocationTargetException */ public Object invoke(Object receiver, Object... args) throws IllegalArgumentException, InvocationTargetException { try { return this.mMethod.invoke(receiver, args); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } /*** * @param name the requested method's name. * @param parameterTypes the parameter types of the requested method. */ HackedMethod(Class<?> cls, String name, Class<?>[] parameterTypes, int modifier) throws HackAssertionException { Method method = null; if (cls == null) { this.mMethod = null; return; } try { method = cls.getDeclaredMethod(name, parameterTypes); if (modifier > 0 && (method.getModifiers() & modifier) != modifier) { Hack.fail(new HackAssertionException(method + " does not match modifiers: " + modifier)); } method.setAccessible(true); } catch (Exception e) { HackAssertionException hackAssertionException = new HackAssertionException(e); hackAssertionException.setHackedClass(cls); hackAssertionException.setHackedMethodName(name); Hack.fail(hackAssertionException); } finally { this.mMethod = method; } } public Method getMethod() { return this.mMethod; } } public static <T> HackedClass<T> into(Class<T> cls) { return new HackedClass(cls); } public static <T> HackedClass<T> into(String str) throws HackAssertionException { try { return new HackedClass(Class.forName(str)); } catch (Exception e) { fail(new HackAssertionException(e)); return new HackedClass(null); } } private static void fail(HackAssertionException hackAssertionException) throws HackAssertionException { if (sFailureHandler == null || !sFailureHandler.onAssertionFailure(hackAssertionException)) { throw hackAssertionException; } } public static void setAssertionFailureHandler( AssertionFailureHandler assertionFailureHandler) { sFailureHandler = assertionFailureHandler; } private Hack() { } }