Package de.geolykt.starloader.deobf

# Class Oaktree

java.lang.Object
de.geolykt.starloader.deobf.Oaktree

public class Oaktree extends Object
Primitive class metadata recovery tool. Originally intended for SML0 (a patch-based modding framework intended for larger tasks like multiplayer) but got recycled to fix the mess produced by the remapping software and to make it ready for decompilation.

Author:
  Geolykt

- ## Field Summary

  Fields
  Modifier and Type
  Field
  Description
  `static final Set<String>`
  `COLLECTIONS`
  A hardcoded set of implementations of the `Collection` interface that apply for generics checking later on.
  `static final Set<String>`
  `ITERABLES`
  A hardcoded set of implementations of the `Iterable` interface that apply for generics checking later on.
  `static final Set<String>`
  `JAVA_KEYWORDS`
  `static final int`
  `VISIBILITY_MODIFIERS`

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `Oaktree()`
  `Oaktree(ClassLoader classWrapperClassloader)`

- ## Method Summary

  Modifier and Type
  Method
  Description
  `Map<MethodReference,ClassWrapper>`
  `analyseLikelyMethodReturnCollectionGenerics()`
  Analyses the likely generic type of the return value of methods.
  `void`
  `applyInnerclasses()`
  Applies the inner class nodes to any encountered classes.
  `void`
  `definalizeAnonymousClasses()`
  Removes the final access modifier from non-obfuscated anonymous classes.
  `void`
  `fixComparators(boolean resolveTRArtifact)`
  Add the signature of obvious bridge methods (i.
  `int`
  `fixForeachOnArray()`
  Resolve useless <unknown> mentions when quiltflower decompiles enhanced for loops that loop on arrays by adding their respective LVT entries via guessing.
  `void`
  `fixInnerClasses()`
  Guesses the inner classes from class nodes
  `void`
  `fixParameterLVT()`
  Method that tries to put the Local Variable Table (LVT) in a acceptable state by synchronising parameter declarations with lvt declarations.
  `int`
  `fixSwitchMaps()`
  Method that tries to restore the SwitchMaps to how they should be.
  `List<org.objectweb.asm.tree.ClassNode>`
  `getClassNodesDirectly()`
  `Map<String,String>`
  `getProposedLocalClassNames(String localClassNamePrefix, BiPredicate<String,String> condition, boolean applyInnerClassNodes)`
  Guesses which classes are a local class within a certain class.
  `static final @Nullable String`
  `getReturnedClass(String methodDesc)`
  Obtains the internal name of the class that is returned by a given method descriptor.
  `Map<String,MethodReference>`
  `guessAnonymousClasses()`
  Guesses the should-be inner classes of classes based on the usages of the class.
  `int`
  `guessAnonymousInnerClasses()`
  Guesses anonymous inner classes by checking whether they have a synthetic field and if they do whether they are referenced only by a single "parent" class.
  `int`
  `guessFieldGenerics()`
  Guesses the generic signatures of fields based on their usage.
  `Map<String,String>`
  `guessLocalClasses()`
  Guesses which classes are local classes within another class.
  `void`
  `index(JarFile file)`
  `void`
  `inferConstructorGenerics()`
  Infers the generics of constructors based on the calls to the constructor.
  `int`
  `inferMethodGenerics()`
  Infers the generic signatures of methods based on the contents of the method.
  `void`
  `invalidateNameCaches()`
  Invalidate internal `ClassNode` `name` caches.
  `void`
  `lambdaStreamGenericSignatureGuessing(Map<FieldReference,ClassWrapper> fields, Map<MethodReference,ClassWrapper> methods)`
  `static void`
  `main(@NotNull String[] args)`
  `void`
  `write(@NotNull OutputStream out, @NotNull Path resources)`
  Write all class nodes and copy over all resources from a given jar.
  `void`
  `write(OutputStream out)`

  ### Methods inherited from class java.lang.Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Field Details

  - ### COLLECTIONS

    public static final Set<String> COLLECTIONS
    A hardcoded set of implementations of the `Collection` interface that apply for generics checking later on.

  - ### ITERABLES

    public static final Set<String> ITERABLES
    A hardcoded set of implementations of the `Iterable` interface that apply for generics checking later on.

  - ### JAVA_KEYWORDS

    public static final Set<String> JAVA_KEYWORDS

  - ### VISIBILITY_MODIFIERS

    public static final int VISIBILITY_MODIFIERS
    See Also:
      - Constant Field Values

- ## Constructor Details

  - ### Oaktree

    public Oaktree()

  - ### Oaktree

    public Oaktree(ClassLoader classWrapperClassloader)

- ## Method Details

  - ### getReturnedClass

    @Nullable @Contract(pure=true) public static final @Nullable String getReturnedClass(String methodDesc)
    Obtains the internal name of the class that is returned by a given method descriptor. If not an object (or array) is returned as according to the method descriptor, null is returned by this method. For arrays, the leading "[" are removed - so it is not possible to differ between array and "normal" object.

    This method was created in anticipation of L-World.

    Parameters:
      `methodDesc` - The method descriptor
    Returns:
      The internal name of the object or array returned by the method.

  - ### main

    public static void main(@NotNull @NotNull String[] args)

  - ### applyInnerclasses

    public void applyInnerclasses()
    Applies the inner class nodes to any encountered classes.

  - ### definalizeAnonymousClasses

    public void definalizeAnonymousClasses()
    Removes the final access modifier from non-obfuscated anonymous classes. The reason this is done is because for recompiled galimulator (using Java 17 to compile and target 1.8), the access modifiers differ in this instance. Why exactly this is the case is unknown to me.

  - ### fixComparators

    public void fixComparators(boolean resolveTRArtifact)
    Add the signature of obvious bridge methods (i. e. comparators).
    Parameters:
      `resolveTRArtifact` - Whether to resolve an artifact left over by tiny remapper.

  - ### fixForeachOnArray

    public int fixForeachOnArray()
    Resolve useless <unknown> mentions when quiltflower decompiles enhanced for loops that loop on arrays by adding their respective LVT entries via guessing. However since this requires the knowledge of the array type, this may not always be successful.
    As this modifies the LVT entries, it should be called AFTER `fixParameterLVT()`.
    Returns:
      The amount of added LVTs

  - ### fixInnerClasses

    public void fixInnerClasses()
    Guesses the inner classes from class nodes

  - ### fixParameterLVT

    public void fixParameterLVT()
    Method that tries to put the Local Variable Table (LVT) in a acceptable state by synchronising parameter declarations with lvt declarations. Does not do anything to the LVT if the LVT is declared but empty, which is a sign of the usage of obfuscation tools. It is intended to be used in combination with decompilers such as quiltflower but might not be useful for less naive decompilers such as procyon, which do not decompile into incoherent java code if the LVT is damaged.

  - ### fixSwitchMaps

    public int fixSwitchMaps()
    Method that tries to restore the SwitchMaps to how they should be. This includes marking the SwitchMap classes as anonymous classes, so it is likely that they cannot be referenced afterwards.

    It may prove useful to call methods such as `guessAnonymousInnerClasses()` or `guessLocalClasses()` BEFORE calling this method, as it needs to find the outermost class of all classes. However if a class is not marked as an inner class of another class, then conflicts may occur as the SwitchMa would be an inner class of both classes. This state is nonsensical and hence warnings will be printed if it is encountered.

    Returns:
      The amount of classes who were identified as switch maps.

  - ### getClassNodesDirectly

    public List<org.objectweb.asm.tree.ClassNode> getClassNodesDirectly()

  - ### guessAnonymousClasses

    @Contract(value="-> new", pure=true) public Map<String,MethodReference> guessAnonymousClasses()
    Guesses the should-be inner classes of classes based on the usages of the class. This only guesses anonymous classes based on the code, but not based on the name. It also does not require the synthetic fields for the anonymous classes, so it may do mismatches from time to time.

    It will not overwrite already existing relations and will not perform any changes to the class nodes. Inner class nodes must therefore be applied manually. It is advisable to run `applyInnerclasses()` afterwards.

    Returns:
      The returned map will have the outer class as the key and the outer method as the desc.

  - ### analyseLikelyMethodReturnCollectionGenerics

    public Map<MethodReference,ClassWrapper> analyseLikelyMethodReturnCollectionGenerics()
    Analyses the likely generic type of the return value of methods. The return value may only be an instance of `Collection` or an implementation of it (as defined through `ClassWrapperPool.isImplementingInterface(ClassWrapper, String)`).

    To reduce the imprecision of the used algorithm following filters are put in place:

    - The method must be static
    - The method must not already have a signature
    - The method must not have arguments
    Due to these constraints only few methods will match. Furthermore this method internally uses rather expensive tools such as the `StackWalker` and the `ClassWrapperPool`, which means using this method may not always be beneficial.

    This method does not modify the signatures of methods directly. This operation would need to be done by an external method.

    Returns:
      A map that stores the location of the method as the key and the signature type as the value.

  - ### getProposedLocalClassNames

    public Map<String,String> getProposedLocalClassNames(String localClassNamePrefix, BiPredicate<String,String> condition, boolean applyInnerClassNodes)
    Guesses which classes are a local class within a certain class. Other than anonymous classes, local classes can be references within the entire block. This method only supports guessing local classes within classes (may they be nested or not), however can also lead to it marking anonymous classes as local classes.

    The names of the local classes are inferred from outer class, the given prefix and their name compared to other local classes.

    This method does not check for collision

    This method will internally allow for large amounts of nesting - at performance cost, however should a recursive nest be assumed, the nest (and all nests depending on the nest) will be silently discarded.

    Parameters:
      `localClassNamePrefix` - The prefix to use for naming local classes
      `condition` - A predicate that is called for each proposed mapping. First argument is the outer class, second argument the inner class. Should the predicate yield false, the relation is discarded.
      `applyInnerClassNodes` - Whether to apply `InnerClassNode` to the inner AND outer class nodes. Making it false may require `fixInnerClasses()` to be applied.
    Returns:
      A map storing proposed renames of the inner classes.

  - ### guessAnonymousInnerClasses

    public int guessAnonymousInnerClasses()
    Guesses anonymous inner classes by checking whether they have a synthetic field and if they do whether they are referenced only by a single "parent" class. Note: this method is VERY aggressive when it comes to adding inner classes, sometimes it adds inner classes on stuff where it wouldn't belong. This means that usage of this method should be done wisely. This method will do some damage even if it does no good.
    Returns:
      The amount of guessed anonymous inner classes

  - ### guessFieldGenerics

    public int guessFieldGenerics()
    Guesses the generic signatures of fields based on their usage. This might be inaccurate under some circumstances, however it tries to play it as safe as possible. The main algorithm in this method checks for generics via foreach iteration over the field and searches for the CHECKCAST to determine the signature. Note: this method should be invoked AFTER `fixParameterLVT()`, invoking this method before it would lead to LVT fixing not working properly
    Returns:
      The amount of added field signatures

  - ### guessLocalClasses

    public Map<String,String> guessLocalClasses()
    Guesses which classes are local classes within another class. Local classes are non-static classes that are nested within another class or method.

    This method is rather aggressive and may recommend pairing that do not make sense. It is up to the API consumer to sort out nonsensical pairings from the ones that make sense.

    Invoking this method does not have any effect in itself. The returned map should be used to create pairings.

    Returns:
      A map that has the inner classes as the key and their outer class as it's value.

  - ### index

    public void index(JarFile file)

  - ### inferConstructorGenerics

    public void inferConstructorGenerics()
    Infers the generics of constructors based on the calls to the constructor.

  - ### inferMethodGenerics

    public int inferMethodGenerics()
    Infers the generic signatures of methods based on the contents of the method.
    Returns:
      The amount of guessed signatures

  - ### invalidateNameCaches

    public void invalidateNameCaches()
    Invalidate internal `ClassNode` `name` caches. Should be invoked when for example class nodes are remapped, at which point internal caches are no longer valid.

  - ### lambdaStreamGenericSignatureGuessing

    public void lambdaStreamGenericSignatureGuessing(Map<FieldReference,ClassWrapper> fields, Map<MethodReference,ClassWrapper> methods)

  - ### write

    public void write(OutputStream out) throws IOException
    Throws:
      `IOException`

  - ### write

    public void write(@NotNull @NotNull OutputStream out, @NotNull @NotNull Path resources) throws IOException
    Write all class nodes and copy over all resources from a given jar. This method throws an IOException if there is no file at the path "resources" or if it is not a zip (and by extension jar) file. If no resources need to be copied over, `write(OutputStream)` should be used instead.
    Parameters:
      `out` - The stream to write the nodes and resources to as a jar
      `resources` - The path to obtain resources from
    Throws:
      `IOException` - If something went wrong while writing to the stream or reading the resources jar.