org.granitemc.granite.utils.Mappings.java Source code

Java tutorial

Introduction

Here is the source code for org.granitemc.granite.utils.Mappings.java

Source

package org.granitemc.granite.utils;

/*
 * License (MIT)
 *
 * Copyright (c) 2014. Granite Team
 *
 * 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:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * 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.
 */

import com.github.kevinsawicki.http.HttpRequest;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.typesafe.config.*;
import javassist.*;
import org.apache.commons.lang3.ArrayUtils;
import org.granitemc.granite.GraniteServerConfig;
import org.granitemc.granite.api.Granite;
import org.granitemc.granite.reflect.ReflectionUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class Mappings {
    static Config file;

    // Human -> Obfuscated Class
    static BiMap<String, Class<?>> classes;
    static BiMap<String, CtClass> ctClasses;

    // Class -> <Signature -> Obfuscated Signature>
    //static Map<Class<?>, BiMap<SignatureParser.MethodSignature, String>> methodNames;

    // Class -> <Name -> Method>
    static Map<CtClass, BiMap<String, MethodHandle>> methods;
    static Map<CtClass, BiMap<String, CtMethod>> ctMethods;
    // TODO: this BiMap may cause an issue with method overloads

    // Class -> <Human Name -> Method>
    static Map<CtClass, BiMap<String, Field>> fields;
    static Map<CtClass, BiMap<String, CtField>> ctFields;

    static ClassPool pool;

    public static void load() {
        try {
            File mappingsFile = new File(Granite.getServerConfig().getMappingsFile().getAbsolutePath());

            String url = "https://raw.githubusercontent.com/GraniteTeam/GraniteMappings/master/1.8.1.json";

            if (Granite.getServerConfig().getAutomaticMappingsUpdating()) {
                Granite.getLogger().info("Querying " + url + " for updates");
                HttpRequest req = HttpRequest.get(url);
                if (!mappingsFile.exists()
                        || !Objects.equals(req.eTag(), Granite.getServerConfig().getLatestMappingsEtag())) {
                    Granite.getLogger().info("Could not find mappings.json (or etag didn't match)");
                    Granite.getLogger().info("Downloading from " + url);

                    if (req.code() == 404) {
                        throw new RuntimeException(
                                "Cannot find mappings file on either the local system or on GitHub. Try placing a mappings.json file in the root server directory.");
                    } else if (req.code() == 200) {
                        req.receive(mappingsFile);
                        ((GraniteServerConfig) Granite.getServerConfig()).file.put("latest-mappings-etag",
                                req.eTag());
                        ((GraniteServerConfig) Granite.getServerConfig()).file.save();
                    }
                }
            }

            file = ConfigFactory.parseReader(new InputStreamReader(new FileInputStream(mappingsFile)),
                    ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON));
        } catch (java.io.IOException e) {
            e.printStackTrace();
        }

        classes = HashBiMap.create();
        ctClasses = HashBiMap.create();

        methods = new HashMap<>();
        ctMethods = new HashMap<>();

        fields = new HashMap<>();
        ctFields = new HashMap<>();

        pool = new ClassPool(true);

        try {
            for (Map.Entry<String, ConfigValue> classObject : file.getObject("classes").entrySet()) {
                String className = (String) ((ConfigObject) classObject.getValue()).get("name").unwrapped();
                ctClasses.put(classObject.getKey(), pool.get(className));
                ctClasses.put(classObject.getKey() + "[]", pool.get(className + "[]"));
            }

            for (Map.Entry<String, CtClass> entry : ctClasses.entrySet()) {
                CtClass ctClass = entry.getValue();
                if (!entry.getKey().endsWith("[]")) {
                    ConfigObject classObject = file
                            .getObject("classes." + entry.getKey().replaceAll("\\$", "\"\\$\""));

                    methods.put(ctClass, HashBiMap.<String, MethodHandle>create());
                    ctMethods.put(ctClass, HashBiMap.<String, CtMethod>create());

                    if (classObject.containsKey("methods")) {
                        for (Map.Entry<String, ConfigValue> methodEntry : ((ConfigObject) classObject
                                .get("methods")).entrySet()) {
                            String methodSignature = methodEntry.getKey();
                            String methodName = (String) methodEntry.getValue().unwrapped();

                            SignatureParser.MethodSignature obfSig = SignatureParser.parseJvm(methodSignature);

                            /*MethodHandle mh = null;
                            try {
                            mh = MethodHandles.lookup().findVirtual(clazz.getValue(), methodSignature.split("\\(")[0], MethodType.methodType(obfSig.getReturnType(), obfSig.getParamTypes()));
                            } catch (NoSuchMethodException | IllegalAccessException e) {
                            if (e.getMessage().startsWith("no such method")) {
                                try {
                                    Method m = clazz.getValue().getDeclaredMethod(methodSignature.split("\\(")[0], obfSig.getParamTypes());
                                    m.setAccessible(true);
                                    mh = MethodHandles.lookup().unreflect(m);
                                } catch (NoSuchMethodException | IllegalAccessException e1) {
                                    e1.printStackTrace();
                                }
                            }
                            }
                                
                            if (mh == null) {
                            mh = mh;
                            }*/

                            CtMethod method = ctClass.getMethod(methodSignature.split("\\(")[0],
                                    "(" + methodSignature.split("\\(")[1]);
                            ctMethods.get(method.getDeclaringClass()).put(methodName, method);
                        }
                    }

                    fields.put(ctClass, HashBiMap.<String, Field>create());
                    ctFields.put(ctClass, HashBiMap.<String, CtField>create());

                    if (classObject.containsKey("fields")) {
                        for (Map.Entry<String, ConfigValue> fieldEntry : ((ConfigObject) classObject.get("fields"))
                                .entrySet()) {
                            String obfuscatedFieldName = fieldEntry.getKey();
                            String fieldName = (String) fieldEntry.getValue().unwrapped();

                            ctFields.get(ctClass).put(fieldName, ctClass.getDeclaredField(obfuscatedFieldName));
                        }
                    }
                }
            }
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
    }

    public static CtClass getCtClass(String humanClassName) {
        return ctClasses.get(humanClassName);
    }

    public static CtClass getCtClass(Class<?> clazz) {
        try {
            return pool.get((clazz.getName().contains("$$") ? clazz.getSuperclass() : clazz).getName());
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String getClassName(CtClass ctClass) {
        return ctClasses.inverse().get(ctClass);
    }

    public static String getClassName(Class<?> clazz) {
        if (classes.inverse().containsKey(clazz)) {
            return classes.inverse().get(clazz);
        } else {
            try {
                return getClassName(pool.get(clazz.getName()));
            } catch (NotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static Class<?> getClass(String humanClassName) {
        if (classes.containsKey(humanClassName)) {
            return classes.get(humanClassName);
        } else {
            try {
                Class<?> clazz = Class.forName(ctClasses.get(humanClassName).getName());
                classes.put(humanClassName, clazz);
                return clazz;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static Field getField(CtClass clazz, String humanFieldName) {
        if (fields.containsKey(clazz)) {
            if (fields.get(clazz).containsKey(humanFieldName)) {
                return fields.get(clazz).get(humanFieldName);
            } else {
                try {
                    CtField ctField = ctFields.get(clazz).get(humanFieldName);
                    if (ctField == null && !clazz.getName().equals("java.lang.Object")) {
                        return getField(clazz.getSuperclass(), humanFieldName);
                    }
                    Field field = getClass(getClassName(ctField.getDeclaringClass()))
                            .getDeclaredField(ctField.getName());
                    fields.get(clazz).put(humanFieldName, field);
                    field.setAccessible(true);
                    return field;
                } catch (NoSuchFieldException | NotFoundException e) {
                    e.printStackTrace();
                }
            }
        } else if (!clazz.getName().equals("java.lang.Object")) {
            try {
                return getField(clazz.getSuperclass(), humanFieldName);
            } catch (NotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static Field getField(String clazz, String humanFieldName) {
        return getField(getCtClass(clazz), humanFieldName);
    }

    public static Field getField(Class<?> clazz, String humanFieldName) {
        return getField(getCtClass(clazz), humanFieldName);
    }

    public static MethodHandle getMethod(CtClass clazz, String methodName) {
        if (methods.containsKey(clazz)) {
            if (methods.get(clazz).containsKey(methodName)) {
                return methods.get(clazz).get(methodName);
            } else {
                CtMethod ctMethod = ctMethods.get(clazz).get(methodName);

                if (ctMethod != null) {
                    try {
                        Class<?>[] paramTypes = new Class<?>[ctMethod.getParameterTypes().length];
                        for (int i = 0; i < paramTypes.length; i++) {
                            paramTypes[i] = ReflectionUtils
                                    .getClassByName(ctMethod.getParameterTypes()[i].getName());
                        }
                        Method m = Class.forName(ctMethod.getDeclaringClass().getName())
                                .getDeclaredMethod(ctMethod.getName(), paramTypes);
                        m.setAccessible(true);
                        MethodHandle handle = MethodHandles.lookup().unreflect(m);

                        methods.get(clazz).put(methodName, handle);
                        return handle;
                    } catch (NotFoundException | NoSuchMethodException | IllegalAccessException
                            | ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        try {
            CtClass superClass = clazz.getSuperclass();
            if (superClass == null || superClass.getName().equals("java.lang.Object")) {
                for (CtClass interfac : clazz.getInterfaces()) {
                    MethodHandle m = getMethod(interfac, methodName);
                    return m;
                }
            } else {
                return getMethod(superClass, methodName);
            }
        } catch (NotFoundException ignored) {
        }
        return null;
    }

    public static MethodHandle getMethod(String clazz, String methodName) {
        return getMethod(getCtClass(clazz), methodName);
    }

    public static MethodHandle getMethod(Class<?> clazz, String methodName) {
        return getMethod(getCtClass(clazz), methodName);
    }

    /*public static String getMethodName(Class<?> clazz, MethodHandle methodHandle) {
    return methods.get(getCtClass(clazz)).inverse().get(methodHandle);
    }
        
    public static String getMethodName(Class<?> clazz, Method method) {
    try {
        return getMethodName(clazz, MethodHandles.lookup().unreflect(method));
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;
    }*/

    public static Object invoke(Object object, String methodName, Object... args) {
        return invoke(object, getMethod(getCtClass(object.getClass()), methodName), args);
    }

    public static Object invoke(Object object, MethodHandle handle, Object... args) {
        try {
            return handle.invokeWithArguments(ArrayUtils.add(args, 0, object));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    public static Object invokeStatic(Class<?> clazz, String methodName, Object... args) {
        try {
            return getMethod(getCtClass(clazz), methodName).invokeWithArguments(args);
        } catch (Throwable throwable) {
            throw new RuntimeException(throwable);
        }
    }

    public static Object invokeStatic(String className, String methodName, Object... args) {
        return invokeStatic(getClass(className), methodName, args);
    }
}