// Path: instrumentation/src/main/java/eu/mikroskeem/shuriken/instrumentation/Descriptor.java
// public final class Descriptor {
//     /** Default method descriptor */
//     public final static String DEFAULT = "()V";
// 
//     private String accepts = "";
//     private String returns = "V";
//     private final String finalString = "(%s)%s";
// 
//     /**
//      * Get new descriptor builder instance
//      *
//      * @return {@link Descriptor} instance
//      * @deprecated Use constructor instead
//      */
//     @Deprecated
//     @Contract(" -> !null")
//     public static Descriptor newDescriptor() {
//         return new Descriptor();
//     }
// 
//     /**
//      * Build method accepts part
//      *
//      * @param arguments Types what method accepts
//      * @return this {@link Descriptor} instance
//      */
//     @NotNull
//     public Descriptor accepts(@NotNull String... arguments) {
//         StringBuilder builder = new StringBuilder();
//         for (String argument : arguments) builder.append(argument);
//         this.accepts = builder.toString();
//         return this;
//     }
// 
//     /**
//      * Build method returns part (default is primitive {@link Void})
//      *
//      * @param arguments Types what method accepts
//      * @return this {@link Descriptor} instance
//      */
//     @NotNull
//     public Descriptor accepts(@NotNull Class<?>... arguments) {
//         return accepts(Stream.of(arguments).map(Type::getDescriptor).toArray(String[]::new));
//     }
// 
//     /**
//      * Build method returns part (default is primitive {@link Void})
//      *
//      * @param arguments Types what method accepts
//      * @return this {@link Descriptor} instance
//      */
//     @NotNull
//     public Descriptor accepts(@NotNull Type... arguments) {
//         return accepts(Stream.of(arguments).map(Type::getDescriptor).toArray(String[]::new));
//     }
// 
//     /**
//      * Build method returns part (default is primitive {@link Void})
//      *
//      * @param arguments Types what method accepts
//      * @return this {@link Descriptor} instance
//      */
//     @NotNull
//     public Descriptor returns(@NotNull String... arguments) {
//         StringBuilder builder = new StringBuilder();
//         for (String argument : arguments) builder.append(argument);
//         this.returns = builder.toString();
//         return this;
//     }
// 
//     /**
//      * Build method returns part (default is primitive {@link Void})
//      *
//      * @param arguments Types what method accepts
//      * @return this {@link Descriptor} instance
//      */
//     @NotNull
//     public Descriptor returns(@NotNull Class<?>... arguments) {
//         return returns(Stream.of(arguments).map(Type::getDescriptor).toArray(String[]::new));
//     }
// 
//     /**
//      * Build method returns part (default is primitive {@link Void})
//      *
//      * @param arguments Types what method accepts
//      * @return this {@link Descriptor} instance
//      */
//     @NotNull
//     public Descriptor returns(@NotNull Type... arguments) {
//         return returns(Stream.of(arguments).map(Type::getDescriptor).toArray(String[]::new));
//     }
// 
//     /**
//      * Builds descriptor
//      *
//      * @return Descriptor string
//      */
//     @NotNull
//     public String build() {
//         return String.format(finalString, accepts, returns);
//     }
// 
//     /**
//      * Builds descriptor
//      *
//      * @return Descriptor string
//      */
//     @Override
//     public String toString() {
//         return build();
//     }
// }
// 
// Path: instrumentation/src/main/java/eu/mikroskeem/shuriken/instrumentation/bytecode/ClassManipulation.java
// @Nullable
// public static MethodNode findMethodWithAccessAtleast(@NotNull List<MethodNode> methodNodes, int access, @NotNull String name, @NotNull String desc) {
//     return findMethod(methodNodes, m -> (m.access & access) != 0 && name.equals(m.name) && desc.equals(m.desc));
// }
// 
// Path: instrumentation/src/main/java/eu/mikroskeem/shuriken/instrumentation/bytecode/ClassManipulation.java
// @NotNull
// public static ClassNode readClass(@NotNull byte[] classData, int flags) {
//     ClassReader classReader = new ClassReader(classData);
//     ClassNode classNode = new ClassNode();
//     classReader.accept(classNode, flags);
//     return classNode;
// }


import eu.mikroskeem.shuriken.instrumentation.Descriptor;
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import java.lang.instrument.Instrumentation;
import java.util.List;
import static eu.mikroskeem.shuriken.instrumentation.bytecode.ClassManipulation.findMethodWithAccessAtleast;
import static eu.mikroskeem.shuriken.instrumentation.bytecode.ClassManipulation.readClass;
package eu.mikroskeem.shuriken.instrumentation.runtime;




/**
 * @author Mark Vainomaa
 */
final class AgentClassValidator {
    private final static Type STRING = Type.getType(String.class);
    private final static Type INSTRUMENTATION = Type.getType(Instrumentation.class);

    private final static String SIGNATURE = new Descriptor().accepts(STRING, INSTRUMENTATION).build();

    static void validateMainClass(@NotNull AgentJarOutputStream outputStream) {
        ClassNode classNode = readClass(outputStream.currentEntryData.toByteArray());
        @SuppressWarnings("unchecked")
        List<MethodNode> methods = (List<MethodNode>) classNode.methods;

        // Try to find agentmain(String, Instrumentation)
// Path: instrumentation/src/main/java/eu/mikroskeem/shuriken/instrumentation/Descriptor.java
// public final class Descriptor {
//     /** Default method descriptor */
//     public final static String DEFAULT = "()V";
// 
//     private String accepts = "";
//     private String returns = "V";
//     private final String finalString = "(%s)%s";
// 
//     /**
//      * Get new descriptor builder instance
//      *
//      * @return {@link Descriptor} instance
//      * @deprecated Use constructor instead
//      */
//     @Deprecated
//     @Contract(" -> !null")
//     public static Descriptor newDescriptor() {
//         return new Descriptor();
//     }
// 
//     /**
//      * Build method accepts part
//      *
//      * @param arguments Types what method accepts
//      * @return this {@link Descriptor} instance
//      */
//     @NotNull
//     public Descriptor accepts(@NotNull String... arguments) {
//         StringBuilder builder = new StringBuilder();
//         for (String argument : arguments) builder.append(argument);
//         this.accepts = builder.toString();
//         return this;
//     }
// 
//     /**
//      * Build method returns part (default is primitive {@link Void})
//      *
//      * @param arguments Types what method accepts
//      * @return this {@link Descriptor} instance
//      */
//     @NotNull
//     public Descriptor accepts(@NotNull Class<?>... arguments) {
//         return accepts(Stream.of(arguments).map(Type::getDescriptor).toArray(String[]::new));
//     }
// 
//     /**
//      * Build method returns part (default is primitive {@link Void})
//      *
//      * @param arguments Types what method accepts
//      * @return this {@link Descriptor} instance
//      */
//     @NotNull
//     public Descriptor accepts(@NotNull Type... arguments) {
//         return accepts(Stream.of(arguments).map(Type::getDescriptor).toArray(String[]::new));
//     }
// 
//     /**
//      * Build method returns part (default is primitive {@link Void})
//      *
//      * @param arguments Types what method accepts
//      * @return this {@link Descriptor} instance
//      */
//     @NotNull
//     public Descriptor returns(@NotNull String... arguments) {
//         StringBuilder builder = new StringBuilder();
//         for (String argument : arguments) builder.append(argument);
//         this.returns = builder.toString();
//         return this;
//     }
// 
//     /**
//      * Build method returns part (default is primitive {@link Void})
//      *
//      * @param arguments Types what method accepts
//      * @return this {@link Descriptor} instance
//      */
//     @NotNull
//     public Descriptor returns(@NotNull Class<?>... arguments) {
//         return returns(Stream.of(arguments).map(Type::getDescriptor).toArray(String[]::new));
//     }
// 
//     /**
//      * Build method returns part (default is primitive {@link Void})
//      *
//      * @param arguments Types what method accepts
//      * @return this {@link Descriptor} instance
//      */
//     @NotNull
//     public Descriptor returns(@NotNull Type... arguments) {
//         return returns(Stream.of(arguments).map(Type::getDescriptor).toArray(String[]::new));
//     }
// 
//     /**
//      * Builds descriptor
//      *
//      * @return Descriptor string
//      */
//     @NotNull
//     public String build() {
//         return String.format(finalString, accepts, returns);
//     }
// 
//     /**
//      * Builds descriptor
//      *
//      * @return Descriptor string
//      */
//     @Override
//     public String toString() {
//         return build();
//     }
// }
// 
// Path: instrumentation/src/main/java/eu/mikroskeem/shuriken/instrumentation/bytecode/ClassManipulation.java
// @Nullable
// public static MethodNode findMethodWithAccessAtleast(@NotNull List<MethodNode> methodNodes, int access, @NotNull String name, @NotNull String desc) {
//     return findMethod(methodNodes, m -> (m.access & access) != 0 && name.equals(m.name) && desc.equals(m.desc));
// }
// 
// Path: instrumentation/src/main/java/eu/mikroskeem/shuriken/instrumentation/bytecode/ClassManipulation.java
// @NotNull
// public static ClassNode readClass(@NotNull byte[] classData, int flags) {
//     ClassReader classReader = new ClassReader(classData);
//     ClassNode classNode = new ClassNode();
//     classReader.accept(classNode, flags);
//     return classNode;
// }


// Path: instrumentation/src/main/java/eu/mikroskeem/shuriken/instrumentation/runtime/AgentClassValidator.java
import eu.mikroskeem.shuriken.instrumentation.Descriptor;
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import java.lang.instrument.Instrumentation;
import java.util.List;
import static eu.mikroskeem.shuriken.instrumentation.bytecode.ClassManipulation.findMethodWithAccessAtleast;
import static eu.mikroskeem.shuriken.instrumentation.bytecode.ClassManipulation.readClass;

package eu.mikroskeem.shuriken.instrumentation.runtime;




/**
 * @author Mark Vainomaa
 */
final class AgentClassValidator {
    private final static Type STRING = Type.getType(String.class);
    private final static Type INSTRUMENTATION = Type.getType(Instrumentation.class);

    private final static String SIGNATURE = new Descriptor().accepts(STRING, INSTRUMENTATION).build();

    static void validateMainClass(@NotNull AgentJarOutputStream outputStream) {
        ClassNode classNode = readClass(outputStream.currentEntryData.toByteArray());
        @SuppressWarnings("unchecked")
        List<MethodNode> methods = (List<MethodNode>) classNode.methods;

        // Try to find agentmain(String, Instrumentation)
        MethodNode agentMain = findMethodWithAccessAtleast(methods, Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "agentmain", SIGNATURE);