2022-02-07 12:45:18 +01:00
package fr.pandacube.lib.paper.reflect ;
import java.io.IOException ;
import java.io.InputStream ;
import java.io.InputStreamReader ;
import java.io.PrintStream ;
import java.io.StringReader ;
2022-06-13 23:01:48 +02:00
import java.lang.reflect.Constructor ;
import java.lang.reflect.Modifier ;
2022-02-07 12:45:18 +01:00
import java.nio.charset.StandardCharsets ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Collections ;
import java.util.List ;
import java.util.Map ;
import java.util.Objects ;
import java.util.TreeMap ;
import java.util.stream.Collectors ;
import org.bukkit.Bukkit ;
import fr.pandacube.lib.core.util.Log ;
import fr.pandacube.lib.core.util.Reflect ;
import fr.pandacube.lib.core.util.Reflect.ReflectClass ;
import fr.pandacube.lib.core.util.Reflect.ReflectField ;
2022-06-13 23:01:48 +02:00
import fr.pandacube.lib.core.util.Reflect.ReflectMember ;
2022-02-07 12:45:18 +01:00
import fr.pandacube.lib.core.util.Reflect.ReflectMethod ;
import net.fabricmc.mappingio.MappingReader ;
import net.fabricmc.mappingio.format.MappingFormat ;
import net.fabricmc.mappingio.tree.MappingTree ;
import net.fabricmc.mappingio.tree.MemoryMappingTree ;
public class NMSReflect {
private static String OBF_NAMESPACE ;
private static String MOJ_NAMESPACE ;
/* package */ static final Map < String , ClassMapping > CLASSES_BY_OBF = new TreeMap < > ( ) ;
/* package */ static final Map < String , ClassMapping > CLASSES_BY_MOJ = new TreeMap < > ( ) ;
private static Boolean IS_SERVER_OBFUSCATED ;
2022-06-23 19:04:31 +02:00
private static boolean isInit = false ;
2022-02-07 12:45:18 +01:00
2022-07-08 01:31:45 +02:00
public static void init ( ) {
2022-06-22 00:46:06 +02:00
2022-06-23 19:04:31 +02:00
synchronized ( NMSReflect . class ) {
if ( isInit )
return ;
isInit = true ;
}
2022-02-17 22:59:50 +01:00
2022-06-23 23:54:52 +02:00
Log . info ( " [NMSReflect] Initializing NMS obfuscation mapping... " ) ;
2022-02-17 22:59:50 +01:00
try {
2022-06-22 00:46:06 +02:00
ReflectClass < ? > obfHelperClass ;
try {
obfHelperClass = Reflect . ofClass ( " io.papermc.paper.util.ObfHelper " ) ;
OBF_NAMESPACE = ( String ) obfHelperClass . field ( " SPIGOT_NAMESPACE " ) . getStaticValue ( ) ;
MOJ_NAMESPACE = ( String ) obfHelperClass . field ( " MOJANG_PLUS_YARN_NAMESPACE " ) . getStaticValue ( ) ;
} catch ( ReflectiveOperationException e ) {
throw new ReflectiveOperationException ( " Unable to find the Paper ofbuscation mapping class or class members. " , e ) ;
}
2022-02-07 12:45:18 +01:00
2022-06-22 00:46:06 +02:00
List < ClassMapping > mappings = loadMappings ( obfHelperClass ) ;
2022-02-07 12:45:18 +01:00
for ( ClassMapping clazz : mappings ) {
CLASSES_BY_OBF . put ( clazz . obfName , clazz ) ;
CLASSES_BY_MOJ . put ( clazz . mojName , clazz ) ;
}
// determine if the runtime server is obfuscated
ClassNotFoundException exIfUnableToDetermine = null ;
for ( ClassMapping clazz : CLASSES_BY_OBF . values ( ) ) {
if ( clazz . obfName . equals ( clazz . mojName ) // avoid direct collision between obf and unobf class names
| | CLASSES_BY_MOJ . containsKey ( clazz . obfName ) // avoid indirect collision
| | CLASSES_BY_OBF . containsKey ( clazz . mojName ) ) // avoid indirect collision
continue ;
try {
Class . forName ( clazz . obfName ) ;
IS_SERVER_OBFUSCATED = true ;
break ;
} catch ( ClassNotFoundException e ) {
try {
Class . forName ( clazz . mojName ) ;
IS_SERVER_OBFUSCATED = false ;
break ;
} catch ( ClassNotFoundException ee ) {
ee . addSuppressed ( e ) ;
if ( exIfUnableToDetermine = = null )
exIfUnableToDetermine = ee ;
}
}
}
if ( IS_SERVER_OBFUSCATED = = null ) {
2022-06-23 18:48:46 +02:00
throw new IllegalStateException ( " Unable to determine if this server is obfuscated or not " , exIfUnableToDetermine ) ;
2022-02-07 12:45:18 +01:00
}
2022-06-24 00:14:53 +02:00
if ( IS_SERVER_OBFUSCATED ) {
Log . info ( " [NMSReflect] NMS runtime classes are obfuscated. " ) ;
}
else {
Log . info ( " [NMSReflect] NMS runtime classes are mojang mapped. " ) ;
}
2022-02-07 12:45:18 +01:00
2022-06-24 00:50:31 +02:00
int missingRuntimeClasses = 0 ;
2022-02-07 12:45:18 +01:00
for ( ClassMapping clazz : mappings ) {
2022-06-23 18:48:46 +02:00
try {
clazz . cacheReflectClass ( ) ;
2022-06-24 00:50:31 +02:00
} catch ( Throwable e ) {
missingRuntimeClasses + + ;
if ( e instanceof ClassNotFoundException cnfe ) {
2022-06-24 01:24:30 +02:00
Log . warning ( " [NMSReflect] Missing runtime class " + cnfe . getMessage ( ) + ( IS_SERVER_OBFUSCATED ? ( " (moj class: " + clazz . mojName + " ) " ) : " " ) ) ;
2022-06-24 00:50:31 +02:00
}
else {
2022-06-24 01:24:30 +02:00
Log . warning ( " [NMSReflect] Unable to load runtime class " + ( IS_SERVER_OBFUSCATED ? ( clazz . obfName + " (moj class: " + clazz . mojName + " ) " ) : clazz . mojName ) ) ;
Log . warning ( e ) ; // throwable on separate log message due to sometimes the message not showing at all because of this exception
2022-06-24 00:50:31 +02:00
}
2022-06-24 00:17:35 +02:00
CLASSES_BY_OBF . remove ( clazz . obfName ) ;
CLASSES_BY_MOJ . remove ( clazz . mojName ) ;
2022-06-23 18:48:46 +02:00
}
2022-02-07 12:45:18 +01:00
}
2022-06-23 18:48:46 +02:00
2022-06-24 00:50:31 +02:00
if ( missingRuntimeClasses > 0 ) {
2022-06-24 01:24:30 +02:00
Log . warning ( " [NMSReflect] " + missingRuntimeClasses + " class have been removed from the mapping data due to the previously stated errors. " ) ;
2022-06-24 00:50:31 +02:00
}
2022-06-23 23:54:52 +02:00
2022-06-22 00:46:06 +02:00
} catch ( Throwable t ) {
2022-06-24 00:50:31 +02:00
CLASSES_BY_OBF . clear ( ) ;
CLASSES_BY_MOJ . clear ( ) ;
2022-06-24 01:24:30 +02:00
Log . severe ( " [NMSReflect] The plugin will not have access to NMS stuff due to an error while loading the obfuscation mapping. " , t ) ;
2022-02-07 12:45:18 +01:00
}
2022-06-24 00:17:35 +02:00
Log . info ( " [NMSReflect] Obfuscation mapping loaded for " + CLASSES_BY_OBF . size ( ) + " classes. " ) ;
2022-02-07 12:45:18 +01:00
}
/ * *
* @param mojName the binary name of the desired class , on the mojang mapping .
* @throws NullPointerException if there is no mapping for the provided Mojang mapped class .
* @throws ClassNotFoundException if there is a mapping , but the runtime class was not found .
* /
public static ClassMapping mojClass ( String mojName ) throws ClassNotFoundException {
2022-06-23 18:48:46 +02:00
return Objects . requireNonNull ( CLASSES_BY_MOJ . get ( mojName ) , " Unable to find the Mojang mapped class ' " + mojName + " ' " ) ;
2022-02-07 12:45:18 +01:00
}
2022-06-22 00:46:06 +02:00
private static List < ClassMapping > loadMappings ( ReflectClass < ? > obfHelperClass ) throws IOException {
try ( final InputStream mappingsInputStream = obfHelperClass . get ( ) . getClassLoader ( ) . getResourceAsStream ( " META-INF/mappings/reobf.tiny " ) ) {
2022-02-07 12:45:18 +01:00
if ( mappingsInputStream = = null ) {
2022-02-17 22:59:50 +01:00
throw new RuntimeException ( " Unable to find the ofbuscation mapping file in the Paper jar. " ) ;
2022-02-07 12:45:18 +01:00
}
MemoryMappingTree tree = new MemoryMappingTree ( ) ;
MappingReader . read ( new InputStreamReader ( mappingsInputStream , StandardCharsets . UTF_8 ) , MappingFormat . TINY_2 , tree ) ;
List < ClassMapping > classes = new ArrayList < > ( ) ;
for ( MappingTree . ClassMapping cls : tree . getClasses ( ) ) {
classes . add ( new ClassMapping ( cls ) ) ;
}
return classes ;
}
}
public static void printHTMLMapping ( PrintStream out ) {
String title = " Obfuscation mapping - " + Bukkit . getName ( ) + " version " + Bukkit . getVersion ( ) ;
out . println ( " <!DOCTYPE html><html><head> \ n "
+ " <title> " + title + " </title> \ n "
+ """
< style >
2022-02-18 19:12:01 +01:00
html {
background - color : # 2F2F2F ;
color : white ;
2022-06-13 23:01:48 +02:00
font - size : 14px ;
2022-06-15 17:05:28 +02:00
font - family : Consolas , monospace ;
}
a : not ( . cl ) {
color : # 1290C3 ;
2022-02-18 19:12:01 +01:00
}
2022-02-07 12:45:18 +01:00
table {
border - collapse : collapse ;
2022-02-18 19:12:01 +01:00
width : 100 % ;
2022-02-07 12:45:18 +01:00
margin : auto ;
}
tr : nth - child ( 2n ) {
2022-02-18 19:12:01 +01:00
background - color : # 373737 ;
2022-02-07 12:45:18 +01:00
}
tr : hover {
2022-02-18 19:12:01 +01:00
background - color : # 555 ;
2022-02-07 12:45:18 +01:00
}
tr > * : first - child {
padding - right : . 5em ;
2022-06-15 00:17:30 +02:00
white - space : nowrap ;
}
2022-06-15 17:05:28 +02:00
b . pu {
2022-06-15 00:17:30 +02:00
color : # 0C0 ;
}
2022-06-15 17:05:28 +02:00
b . pt {
color : # FC0 ;
2022-06-15 00:17:30 +02:00
}
2022-06-15 17:05:28 +02:00
b . pv {
color : # F00 ;
2022-06-15 00:17:30 +02:00
}
2022-06-15 17:05:28 +02:00
b . pk {
color : # 66F ;
2022-02-07 12:45:18 +01:00
}
td {
padding - top : 0 ;
padding - bottom : 0 ;
}
th {
text - align : left ;
font - size : 1 . 1em ;
2022-02-18 19:12:01 +01:00
border - top : solid 1px white ;
2022-02-07 12:45:18 +01:00
}
2022-02-18 19:12:01 +01:00
. kw {
color : # CC6C1D ;
}
. cl {
color : # 1290C3 ;
}
. mtd {
color : # 1EB540 ;
}
. fld {
color : # 8DDAF8 ;
2022-02-07 12:45:18 +01:00
}
2022-06-13 23:01:48 +02:00
. st {
font - style : italic ;
}
. st . fn {
font - weight : bold ;
}
2022-02-07 12:45:18 +01:00
< / style >
2022-06-15 17:05:28 +02:00
< / head > < body >
2022-02-07 12:45:18 +01:00
"""
+ " <h1> " + title + " </h1> \ n "
2022-06-15 17:05:28 +02:00
+ """
< p >
< b > C < / b > : < span class = ' kw ' > class < / span > & nbsp ; & nbsp ;
< b > E < / b > : < span class = ' kw ' > enum < / span > & nbsp ; & nbsp ;
< b > I < / b > : < span class = ' kw ' > interface < / span > & nbsp ; & nbsp ;
< b > @ < / b > : < span class = ' kw ' > @interface < / span > & nbsp ; & nbsp ;
< b > R < / b > : < span class = ' kw ' > record < / span > < br >
< b > ● < / b > : field & nbsp ; & nbsp ;
< b > c < / b > : constructor & nbsp ; & nbsp ;
< b > ⬤ < / b > : method < br >
< b class = ' pu ' > ⬤ < / b > : < span class = ' kw ' > public < / span > & nbsp ; & nbsp ;
< b class = ' pt ' > ⬤ < / b > : < span class = ' kw ' > protected < / span > & nbsp ; & nbsp ;
< b class = ' pk ' > ⬤ < / b > : package & nbsp ; & nbsp ;
< b class = ' pv ' > ⬤ < / b > : < span class = ' kw ' > private < / span > < br >
< sup > S < / sup > : < span class = ' kw ' > static < / span > & nbsp ; & nbsp ;
< sup > A < / sup > : < span class = ' kw ' > abstract < / span > & nbsp ; & nbsp ;
< sup > F < / sup > : < span class = ' kw ' > final < / span >
< / p >
< table >
" " " );
2022-02-18 19:12:01 +01:00
out . println ( " <tr><th>ns</th><th> " + OBF_NAMESPACE + " </th><th> " + MOJ_NAMESPACE + " </th></tr> " ) ;
2022-02-07 12:45:18 +01:00
for ( ClassMapping clazz : CLASSES_BY_OBF . values ( ) ) {
clazz . printHTML ( out ) ;
}
2022-02-18 19:12:01 +01:00
out . println ( " </table><p>Generated by <a href='https://github.com/marcbal'>marcbal</a> "
+ " using <a href='https://github.com/PandacubeFr/PandaLib/blob/master/Paper/src/main/java/fr/pandacube/lib/paper/reflect/NMSReflect.java'>this tool</a> "
+ " running on <a href='https://papermc.io/'> " + Bukkit . getName ( ) + " </a> version " + Bukkit . getVersion ( ) + " </p> "
+ " </body></html> " ) ;
2022-02-07 12:45:18 +01:00
}
public static class ClassMapping {
private static int nextID = 0 ;
/* package */ final int id = nextID + + ;
/* package */ final String obfName ;
/* package */ final String mojName ;
2022-06-13 23:01:48 +02:00
private final Map < MethodId , MemberMapping < MethodId , ReflectMethod < ? > > > methodsByObf = new TreeMap < > ( ) ;
private final Map < MethodId , MemberMapping < MethodId , ReflectMethod < ? > > > methodsByMoj = new TreeMap < > ( ) ;
private final Map < String , MemberMapping < String , ReflectField < ? > > > fieldsByObf = new TreeMap < > ( ) ;
private final Map < String , MemberMapping < String , ReflectField < ? > > > fieldsByMoj = new TreeMap < > ( ) ;
2022-02-07 12:45:18 +01:00
private ReflectClass < ? > runtimeReflectClass = null ;
private ClassMapping ( MappingTree . ClassMapping cls ) {
obfName = binaryClassName ( cls . getName ( OBF_NAMESPACE ) ) ;
mojName = binaryClassName ( cls . getName ( MOJ_NAMESPACE ) ) ;
cls . getMethods ( ) . stream ( ) . map ( MemberMapping : : of ) . forEach ( method - > {
2022-06-13 23:01:48 +02:00
method . declaringClass = this ;
2022-02-07 12:45:18 +01:00
methodsByObf . put ( method . obfDesc . identifier , method ) ;
methodsByMoj . put ( method . mojDesc . identifier , method ) ;
} ) ;
cls . getFields ( ) . stream ( ) . map ( MemberMapping : : of ) . forEach ( field - > {
2022-06-13 23:01:48 +02:00
field . declaringClass = this ;
2022-02-07 12:45:18 +01:00
fieldsByObf . put ( field . obfDesc . identifier , field ) ;
fieldsByMoj . put ( field . mojDesc . identifier , field ) ;
} ) ;
}
private synchronized void cacheReflectClass ( ) throws ClassNotFoundException {
if ( runtimeReflectClass = = null )
runtimeReflectClass = Reflect . ofClass ( IS_SERVER_OBFUSCATED ? obfName : mojName ) ;
}
public ReflectClass < ? > runtimeReflect ( ) {
return runtimeReflectClass ;
}
public Class < ? > runtimeClass ( ) {
return runtimeReflectClass . get ( ) ;
}
/ * *
*
* @param mojName the Mojang mapped name of the method .
* @param mojParametersType the list of parameters of the method .
* Each parameter type must be an instance of one of the following type :
* { @link Type } , { @link Class } , { @link ReflectClass } or { @link ClassMapping } .
* @return
* @throws IllegalArgumentException if one of the parameter has an invalid type
* @throws NullPointerException if one of the parameter is null , or if there is no mapping for the provided Mojang mapped method .
* @throws ClassNotFoundException if there is no runtime class to represent one of the provided parametersType .
* @throws NoSuchMethodException if there is no runtime method to represent the provided method .
* /
public ReflectMethod < ? > mojMethod ( String mojName , Object . . . mojParametersType ) throws ClassNotFoundException , NoSuchMethodException {
MethodId mId = new MethodId ( mojName , Type . toTypeList ( Arrays . asList ( mojParametersType ) ) ) ;
2022-06-13 23:01:48 +02:00
MemberMapping < MethodId , ReflectMethod < ? > > mm = methodsByMoj . get ( mId ) ;
2022-02-07 12:45:18 +01:00
Objects . requireNonNull ( mm , " Unable to find the Mojang mapped method " + mId ) ;
2022-06-13 23:01:48 +02:00
try {
return mm . getReflectMember ( ) ;
} catch ( ReflectiveOperationException e ) {
if ( e instanceof ClassNotFoundException cnfe )
throw cnfe ;
if ( e instanceof NoSuchMethodException nsme )
throw nsme ;
// should not have another exception
throw new RuntimeException ( e ) ;
}
2022-02-07 12:45:18 +01:00
}
/ * *
*
* @param mojName the Mojang mapped name of the field .
* @return
* @throws NullPointerException if there is no mapping for the provided Mojang mapped field .
* @throws NoSuchFieldException if there is no runtime method to represent the provided method .
* /
public ReflectField < ? > mojField ( String mojName ) throws NoSuchFieldException {
2022-06-13 23:01:48 +02:00
MemberMapping < String , ReflectField < ? > > fm = fieldsByMoj . get ( mojName ) ;
2022-02-07 12:45:18 +01:00
Objects . requireNonNull ( fm , " Unable to find the Mojang mapped field ' " + mojName + " ' " ) ;
2022-06-13 23:01:48 +02:00
try {
return fm . getReflectMember ( ) ;
} catch ( ReflectiveOperationException e ) {
if ( e instanceof NoSuchFieldException nsfe )
throw nsfe ;
// should not have another exception
throw new RuntimeException ( e ) ;
}
2022-02-07 12:45:18 +01:00
}
/* package */ String toClickableHTML ( boolean isObfClass ) {
String classToPrint = isObfClass ? obfName : mojName ;
String classSimpleName = classToPrint . substring ( classToPrint . lastIndexOf ( '.' ) + 1 ) ;
String htmlTitle = classSimpleName . equals ( classToPrint ) ? " " : ( " title=' " + classToPrint + " ' " ) ;
2022-02-18 19:12:01 +01:00
String typeHTML = " <a href='#c " + id + " ' " + htmlTitle + " class='cl'> " + classSimpleName + " </a> " ;
2022-02-07 12:45:18 +01:00
return typeHTML ;
}
/* package */ Type toType ( boolean obf ) {
return new Type ( obf ? obfName : mojName , 0 ) ;
}
private void printHTML ( PrintStream out ) {
2022-06-15 00:17:30 +02:00
String modifiersHTML = classModifiersToHTML ( runtimeClass ( ) ) ;
out . println ( " <tr id='c " + id + " '><th> " + modifiersHTML + " </th><th> " + nameToHTML ( true ) + " </th><th> " + nameToHTML ( false ) + " </th></tr> " ) ;
2022-06-15 17:05:28 +02:00
fieldsByObf . values ( ) . stream ( ) . filter ( mm - > mm . isStatic ( ) ) . forEach ( f - > f . printHTML ( out ) ) ;
methodsByObf . values ( ) . stream ( ) . filter ( mm - > mm . isStatic ( ) ) . forEach ( m - > m . printHTML ( out ) ) ;
2022-06-13 23:01:48 +02:00
printConstructorsHTML ( out ) ;
2022-06-15 17:05:28 +02:00
fieldsByObf . values ( ) . stream ( ) . filter ( mm - > ! mm . isStatic ( ) ) . forEach ( f - > f . printHTML ( out ) ) ;
methodsByObf . values ( ) . stream ( ) . filter ( mm - > ! mm . isStatic ( ) ) . forEach ( m - > m . printHTML ( out ) ) ;
2022-02-07 12:45:18 +01:00
}
private String nameToHTML ( boolean obf ) {
2022-02-18 19:12:01 +01:00
String classToPrint = obf ? obfName : mojName ;
int packageSep = classToPrint . lastIndexOf ( '.' ) ;
String classSimpleName = classToPrint . substring ( packageSep + 1 ) ;
String classPackages = classToPrint . substring ( 0 , packageSep > 0 ? packageSep : 0 ) ;
String classHTML = ( packageSep > = 0 ? ( classPackages + " . " ) : " " ) + " <b class='cl'> " + classSimpleName + " </b> " ;
2022-02-07 12:45:18 +01:00
Type superClass = superClass ( obf ) ;
2022-02-18 19:12:01 +01:00
String superClassHTML = superClass = = null ? " " : ( " <span class='kw'>extends</span> " + superClass . toHTML ( obf ) ) ;
2022-02-07 12:45:18 +01:00
List < Type > superInterfaces = superInterfaces ( obf ) ;
String superInterfacesHTML = superInterfaces . isEmpty ( ) ? " "
2022-02-18 19:12:01 +01:00
: ( " <span class='kw'>implements</span> " + superInterfaces . stream ( ) . map ( t - > t . toHTML ( obf ) ) . collect ( Collectors . joining ( " , " ) ) ) ;
2022-02-07 12:45:18 +01:00
2022-02-18 19:12:01 +01:00
return classHTML + superClassHTML + superInterfacesHTML ;
2022-02-07 12:45:18 +01:00
}
private Type superClass ( boolean obf ) {
Class < ? > superClass = runtimeClass ( ) . getSuperclass ( ) ;
2022-02-18 19:12:01 +01:00
if ( superClass = = null | | superClass . equals ( Object . class ) | | superClass . equals ( Enum . class ) | | superClass . equals ( Record . class ) )
2022-02-07 12:45:18 +01:00
return null ;
ClassMapping cm = ( IS_SERVER_OBFUSCATED ? CLASSES_BY_OBF : CLASSES_BY_MOJ ) . get ( superClass . getName ( ) ) ;
return ( cm ! = null ) ? cm . toType ( obf ) : Type . of ( superClass ) ;
}
private List < Type > superInterfaces ( boolean obf ) {
Class < ? > [ ] interfaces = runtimeClass ( ) . getInterfaces ( ) ;
List < Type > types = new ArrayList < > ( interfaces . length ) ;
for ( Class < ? > interfce : interfaces ) {
ClassMapping cm = ( IS_SERVER_OBFUSCATED ? CLASSES_BY_OBF : CLASSES_BY_MOJ ) . get ( interfce . getName ( ) ) ;
types . add ( ( cm ! = null ) ? cm . toType ( obf ) : Type . of ( interfce ) ) ;
}
return types ;
}
2022-06-13 23:01:48 +02:00
private void printConstructorsHTML ( PrintStream out ) {
String classObfSimpleName = obfName . substring ( obfName . lastIndexOf ( '.' ) + 1 ) ;
String classMojSimpleName = mojName . substring ( mojName . lastIndexOf ( '.' ) + 1 ) ;
for ( Constructor < ? > ct : runtimeClass ( ) . getDeclaredConstructors ( ) ) {
List < Type > obfParams = new ArrayList < > ( ) ;
List < Type > mojParams = new ArrayList < > ( ) ;
for ( Class < ? > param : ct . getParameterTypes ( ) ) {
ClassMapping cm = ( IS_SERVER_OBFUSCATED ? CLASSES_BY_OBF : CLASSES_BY_MOJ ) . get ( param . getName ( ) ) ;
if ( cm = = null ) {
Type t = Type . of ( param ) ;
obfParams . add ( t ) ;
mojParams . add ( t ) ;
}
else {
obfParams . add ( cm . toType ( true ) ) ;
mojParams . add ( cm . toType ( false ) ) ;
}
}
out . println ( " <tr> "
2022-06-15 00:17:30 +02:00
+ " <td> " + elementModifiersToHTML ( " c " , ct . getModifiers ( ) ) + " </td> "
2022-06-15 17:05:28 +02:00
+ " <td><b class='mtd'> " + classObfSimpleName + " </b>( " + obfParams . stream ( ) . map ( t - > t . toHTML ( true ) ) . collect ( Collectors . joining ( " , " ) ) + " )</td> "
+ " <td><b class='mtd'> " + classMojSimpleName + " </b>( " + mojParams . stream ( ) . map ( t - > t . toHTML ( false ) ) . collect ( Collectors . joining ( " , " ) ) + " )</td> "
2022-06-13 23:01:48 +02:00
+ " </tr> " ) ;
}
}
2022-02-07 12:45:18 +01:00
}
private static record MethodId ( String name , List < Type > parametersType ) implements Comparable < MethodId > {
@Override
public int compareTo ( MethodId o ) {
int cmp = name . compareTo ( o . name ) ;
if ( cmp ! = 0 )
return cmp ;
return toString ( ) . compareTo ( o . toString ( ) ) ;
}
2022-06-13 23:01:48 +02:00
private String toHTML ( boolean isObfClass , boolean isStatic , boolean isFinal ) {
2022-02-07 12:45:18 +01:00
String paramsHTML = parametersType . stream ( ) . map ( p - > p . toHTML ( isObfClass ) ) . collect ( Collectors . joining ( " , " ) ) ;
2022-06-13 23:01:48 +02:00
String cl = " mtd " ;
if ( isStatic )
cl + = " st " ;
if ( isFinal )
cl + = " fn " ;
String identifierHTML = " <span class=' " + cl + " '> " + name + " </span>( " + paramsHTML + " ) " ;
2022-02-07 12:45:18 +01:00
return identifierHTML ;
}
public String toString ( ) {
String paramsStr = parametersType . stream ( ) . map ( Type : : toString ) . collect ( Collectors . joining ( " , " ) ) ;
return name + " ( " + paramsStr + " ) " ;
}
}
private static record MemberDesc < I extends Comparable < I > > ( I identifier , Type returnType ) {
2022-06-15 00:17:30 +02:00
private String toHTML ( boolean isObfClass , boolean isStatic , boolean isFinal ) {
2022-02-07 12:45:18 +01:00
String identifierHTML = " " ;
if ( identifier instanceof MethodId mId )
2022-06-13 23:01:48 +02:00
identifierHTML = mId . toHTML ( isObfClass , isStatic , isFinal ) ;
else if ( identifier instanceof String n ) {
String cl = " fld " ;
if ( isStatic )
cl + = " st " ;
if ( isFinal )
cl + = " fn " ;
identifierHTML = " <span class=' " + cl + " '> " + n + " </span> " ;
}
2022-02-07 12:45:18 +01:00
return returnType . toHTML ( isObfClass ) + " " + identifierHTML ;
}
private static MemberDesc < MethodId > of ( MappingTree . MethodMapping member , String namespace ) {
String desc = member . getDesc ( namespace ) ;
try ( StringReader descReader = new StringReader ( desc ) ) {
char r = ( char ) descReader . read ( ) ;
if ( r ! = '(' )
throw new IllegalArgumentException ( " Invalid method description ' " + desc + " '. Must start with '('. " ) ;
List < Type > paramsType = new ArrayList < > ( ) ;
while ( ( r = ( char ) descReader . read ( ) ) ! = ')' ) {
descReader . skip ( - 1 ) ;
paramsType . add ( Type . parse ( descReader ) ) ;
}
Type retType = Type . parse ( descReader ) ;
return new MemberDesc < > ( new MethodId ( member . getName ( namespace ) , Collections . unmodifiableList ( paramsType ) ) , retType ) ;
} catch ( IOException e ) {
throw new RuntimeException ( " StringReader read error " , e ) ;
}
}
private static MemberDesc < String > of ( MappingTree . FieldMapping member , String namespace ) {
StringReader descReader = new StringReader ( member . getDesc ( namespace ) ) ;
return new MemberDesc < > ( member . getName ( namespace ) , Type . parse ( descReader ) ) ;
}
}
2022-06-13 23:01:48 +02:00
private static abstract class MemberMapping < I extends Comparable < I > , R extends ReflectMember < ? , ? , ? , ? > > {
2022-06-15 00:17:30 +02:00
private String htmlTypeChar ;
2022-02-07 12:45:18 +01:00
/* package */ MemberDesc < I > obfDesc , mojDesc ;
2022-06-13 23:01:48 +02:00
/* package */ ClassMapping declaringClass ;
2022-06-15 00:17:30 +02:00
private MemberMapping ( String htmlType , MemberDesc < I > obfDesc , MemberDesc < I > mojDesc ) {
htmlTypeChar = htmlType ;
2022-02-07 12:45:18 +01:00
this . obfDesc = obfDesc ;
this . mojDesc = mojDesc ;
}
/* package */ void printHTML ( PrintStream out ) {
2022-06-13 23:01:48 +02:00
int mod = 0 ;
try {
mod = getReflectMember ( ) . getModifiers ( ) ;
} catch ( ReflectiveOperationException e ) {
// ignore
}
boolean isStatic = Modifier . isStatic ( mod ) ;
boolean isFinal = Modifier . isFinal ( mod ) ;
out . println ( " <tr> "
2022-06-15 00:17:30 +02:00
+ " <td> " + elementModifiersToHTML ( htmlTypeChar , mod ) + " </td> "
2022-06-13 23:01:48 +02:00
+ " <td> " + obfDesc . toHTML ( true , isStatic , isFinal ) + " </td> "
+ " <td> " + mojDesc . toHTML ( false , isStatic , isFinal ) + " </td> "
+ " </tr> " ) ;
2022-02-07 12:45:18 +01:00
}
2022-06-13 23:01:48 +02:00
/* package */ MemberDesc < I > getReflectDesc ( ) {
return ( IS_SERVER_OBFUSCATED ? obfDesc : mojDesc ) ;
2022-02-07 12:45:18 +01:00
}
2022-06-13 23:01:48 +02:00
/* package */ abstract R getReflectMember ( ) throws ReflectiveOperationException ;
2022-06-15 17:05:28 +02:00
/* package */ boolean isStatic ( ) {
try {
return Modifier . isStatic ( getReflectMember ( ) . getModifiers ( ) ) ;
} catch ( ReflectiveOperationException e ) {
Log . severe ( e ) ;
return false ;
}
}
2022-06-13 23:01:48 +02:00
private static MemberMapping < MethodId , ReflectMethod < ? > > of ( MappingTree . MethodMapping mioMapping ) {
2022-06-15 00:17:30 +02:00
return new MemberMapping < > ( " ⬤ " , MemberDesc . of ( mioMapping , OBF_NAMESPACE ) , MemberDesc . of ( mioMapping , MOJ_NAMESPACE ) ) {
2022-06-13 23:01:48 +02:00
@Override
ReflectMethod < ? > getReflectMember ( ) throws ClassNotFoundException , NoSuchMethodException {
MethodId id = getReflectDesc ( ) . identifier ;
return declaringClass . runtimeReflectClass . method ( id . name , Type . toClassArray ( id . parametersType ) ) ;
}
} ;
}
private static MemberMapping < String , ReflectField < ? > > of ( MappingTree . FieldMapping mioMapping ) {
2022-06-15 00:17:30 +02:00
return new MemberMapping < > ( " ● " , MemberDesc . of ( mioMapping , OBF_NAMESPACE ) , MemberDesc . of ( mioMapping , MOJ_NAMESPACE ) ) {
2022-06-13 23:01:48 +02:00
@Override
ReflectField < ? > getReflectMember ( ) throws NoSuchFieldException {
String id = getReflectDesc ( ) . identifier ;
return declaringClass . runtimeReflectClass . field ( id ) ;
}
} ;
2022-02-07 12:45:18 +01:00
}
2022-06-15 17:05:28 +02:00
2022-02-07 12:45:18 +01:00
}
/* package */ static String binaryClassName ( String cl ) {
return cl . replace ( '/' , '.' ) ;
}
2022-06-15 00:17:30 +02:00
private static String classModifiersToHTML ( Class < ? > clazz ) {
String elementHTMLType ;
if ( clazz . isEnum ( ) )
elementHTMLType = " E " ;
else if ( clazz . isAnnotation ( ) )
elementHTMLType = " @ " ;
else if ( clazz . isInterface ( ) )
elementHTMLType = " I " ;
else if ( clazz . isRecord ( ) )
elementHTMLType = " R " ;
else if ( clazz . isPrimitive ( ) )
elementHTMLType = " " ;
else
elementHTMLType = " C " ;
return elementModifiersToHTML ( elementHTMLType , clazz . getModifiers ( ) ) ;
}
2022-06-13 23:01:48 +02:00
2022-06-15 00:17:30 +02:00
private static String elementModifiersToHTML ( String elementHTMLType , int elModifiers ) {
2022-06-15 17:05:28 +02:00
String html = " <b class=' " ;
2022-06-13 23:01:48 +02:00
if ( Modifier . isPublic ( elModifiers ) )
2022-06-15 17:05:28 +02:00
html + = " pu " ;
2022-06-15 00:17:30 +02:00
else if ( Modifier . isProtected ( elModifiers ) )
2022-06-15 17:05:28 +02:00
html + = " pt " ;
2022-06-15 00:17:30 +02:00
else if ( Modifier . isPrivate ( elModifiers ) )
2022-06-15 17:05:28 +02:00
html + = " pv " ;
2022-06-15 00:17:30 +02:00
else
2022-06-15 17:05:28 +02:00
html + = " pk " ;
2022-06-13 23:01:48 +02:00
2022-06-15 17:05:28 +02:00
html + = " '> " + elementHTMLType + " </b> " ;
boolean isStatic = Modifier . isStatic ( elModifiers ) ;
boolean isAbstract = Modifier . isAbstract ( elModifiers ) ;
boolean isFinal = Modifier . isFinal ( elModifiers ) ;
if ( isStatic | | isAbstract | | isFinal ) {
html + = " <sup> " ;
if ( isStatic )
html + = " S " ;
if ( isAbstract )
html + = " A " ;
if ( isFinal )
html + = " F " ;
html + = " </sup> " ;
}
2022-06-15 00:17:30 +02:00
return html ;
2022-06-13 23:01:48 +02:00
}
2022-06-15 00:17:30 +02:00
// ● (field)
// ⬤ (method)
2022-02-07 12:45:18 +01:00
}