• S'inscrire
    • Se connecter
    • Recherche
    • Récent
    • Mots-clés
    • Populaire
    • Utilisateurs
    • Groupes

    ASM

    Autres
    8
    13
    6387
    Charger plus de messages
    • Du plus ancien au plus récent
    • Du plus récent au plus ancien
    • Les plus votés
    Répondre
    • Répondre à l'aide d'un nouveau sujet
    Se connecter pour répondre
    Ce sujet a été supprimé. Seuls les utilisateurs avec les droits d'administration peuvent le voir.
    • jglrxavpok
      jglrxavpok Modérateurs dernière édition par robin4002

      ATTENTION: Ce tutoriel n’est pas fini et j’aurai besoin de retours sur celui-ci pour le continuer, merci

      Ce tutoriel est général mais peut être facilement utilisé pour Minecraft.

      Présentation de la librairie

      ASM est une librairie qui permet la manipulation de bytecode. Pour info, le bytecode est la version compilée du java (pour faire simple) et il est composé d’instructions simples telles que: ajout de deux nombres, multiplication de deux nombres, chargement d’une valeur en mémoire, etc.

      Vous pouvez télécharger ASM ici: https://search.maven.org/remotecontent?filepath=org/ow2/asm/asm/6.2.1/asm-6.2.1.jar (si vous voulez des versions plus récentes que la 6.2.1, cherchez sur Maven Central)

      Préparation du projet

      Créez un nouveau projet avec le nom que vous voulez (ce sera ASMTuto pour moi) puis ajoutez le fichier “asm-all-5.0.3.jar” à votre Build Path:
      Clic droit sur le project, Build Path, Configure Build Path…, onglet Libraries, appuyez sur le bouton “Add external jar” et choisissez ce fichier dans “/UnCertainDossierOùVousAvezTéléchargéASM/asm-5.0.3-bin/asm-5.0.3/lib/all/asm-all-5.0.3.jar”

      C’est tout ce qu’il y a à faire et vous avez ASM prêt!

      Créer une classe à partir de bytecode

      On commence par créer une instance de ClassWriter avec les options COMPUTE_MAXS et COMPUTRE_FRAMES qui permettent respectivement de laisser ASM gérer le nombre de variables locales et de frames.

      ​ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
      

      Ensuite, on utilise cette instance pour créer la base de la classe.

      Pour créer la classe TestASM suivante:

      ​public class TestASM {}
      

      Il suffit d’appeler la méthode visit de ClassWriter:

      ​classWriter.visit(classVersion, access, name, signature, superclass, interfaces);
      
      • classVersion: La version de la classe, nous utiliserons V1_8 pour Java 8 dans ce tutoriel.
      • access: public, abstract, final, protected, private, [default]
      • name: Le nom de la classe
      • La signature de la classe que l’on va laisser à null la plupart du temps.
      • superclass: La classe parente, “java/lang/Object” si il n’y en a aucune.
      • interfaces: Un tableau de String contenant les types internes des interfaces que la classe implémente.

      Et on utilise les bons arguments pour notre exemple:

      ​classWriter.visit(V1_8, ACC_PUBLIC, "TestASM", null, "java/lang/Object", new String[0]);
      

      Nous avons donc ce code ci maintenant:

      public class ASMMain extends ClassLoader implements Opcodes {
      
          public static void main(String[] args) throws IOException {
              String name = "TestASM";
              ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
              classWriter.visit(V1_8, ACC_PUBLIC, name, null, "java/lang/Object", new String[0]);
      
              if(new File(name + ".class").exists())
                  new File(name + ".class").delete();
              FileOutputStream fos = new FileOutputStream(name+".class");
              fos.write(bytes);
              fos.close();
          }
      }
      

      Et si l’on ouvre le fichier TestASM.class avec JD-Gui par exemple:

      Et voilà!

      Créer une méthode à partir de bytecode

      MethodVisitor ClassWriter.visitMethod(int access, String name, String desc, String signature, String[] exceptions)
      
      • access: public, abstract, final, protected, private, [default]
      • name: Le nom de la méthode.
      • desc: La description de la méthode, on verra ça plus en détail juste après
      • signature: pareil que pour les classes
      • exceptions: Tableau contenant les types internes des exceptions renvoyées par cette méthode

      Nous allons créer cette méthode:

      ​public static void main(String[] args)
      {
              System.out.println("Bonjour tout le monde en ASM!");
      }
      

      Premièrement, on doit dire à ASM qu’on l’on va créer du code:

      ​classWriter.visitSource(name + ".class", null);
      

      Ensuite on crée la méthode avec visitMethod et on récupére l’instance de MethodVisitor retournée:

      ​MethodVisitor mv = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "main", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String[].class)), null, new String[0]);
      
      • ACC_PUBLIC | ACC_STATIC: la méthode sera publique et statique (l’opérateur bitwise | permet d’ajouter les différentes options)
      • “main”: le nom de notre méthode
      • Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String[].class)): Ceci est une méthode utilitaire d’ASM qui permet de créer rapidement la description d’une méthode, ici une méthode qui retourne void (d’où le VOID_TYPE) avec pour arguments une instance de String[] (d’où le Type.getType(String[].class))

      Ensuite on doit indiquer à ASM que l’on commence le code de la méthode:

      mv.visitCode();
      

      On ajoute un return directement pour ne pas laisser la méthode vide pour le moment:

      ​mv.visitInsn(RETURN);
      

      RETURN est un int qui provient d’Opcodes qui correspond à l’instruction

      ​return;
      

      Et on finit par prévenir ASM que l’on a fini:

      ​mv.visitEnd();
      

      On lance le programme et on ouvre la classe dans JD-GUI et on obtient:

      C’est un début!

      🤔 Pourquoi le paramètre String[] à un nom moche ?
      ❗  C’est un nom généré par JD-Gui quand il est incapable de trouver un nom correct, on va corriger ça!

      Les variables locales

      Les variables locales désignent toutes les variables disponibles uniquement au sein des méthodes. Ajoutez à cela les paramètres.
      Chaque variable locale a un index donné, de 0 à n. Elles disposent aussi d’un type et d’un nom.

      Pour créer une nouvelle variable locale, on utilise l’instruction

      void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index)
      
      • name: Le nom de la variable locale
      • desc: la description de la variable
      • signature: comme d’habitude, pas très important pour nous
      • start et end: Instructions pour lesquelles la variable locale est accessible, vides si elle est tout le monde accessible (par exemple les paramètres)
      • index: l’index de la variable locale, en commençant par 0

      Pour créer une variable String[] args, on remplace les valeurs ainsi:

      ​mv.visitLocalVariable("args", Type.getInternalName(String[].class), null, new Label(), new Label(), 0);
      
      • “args”: le nom
      • Type.getInternalName(String[].class): Méthode utilitaire d’ASM pour récupérer le nom d’une classe directement
      • null: la signature
      • new Label() x2: début et fin, tous les deux vides pour signifier l’utilisation dans toute la méthode.
      • 0: l’index, ici on veut le premier argument: “args”

      Fil rouge:

      package fr.mff.asm;
      
      import java.io.*;
      
      import org.objectweb.asm.*;
      
      public class ASMMain extends ClassLoader implements Opcodes {
      
          public static void main(String[] args) throws IOException {
              String name = "TestASM";
              ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
              classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, name, null, "java/lang/Object", new String[0]);
      
              classWriter.visitSource(name + ".class", null);
              MethodVisitor mv = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "main", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String[].class)), null, new String[0]);
              mv.visitCode();
              mv.visitLocalVariable("args", Type.getDescriptor(String[].class), null, new Label(), new Label(), 0);
              mv.visitInsn(RETURN);
              mv.visitEnd();
      
              ASMMain instance = new ASMMain();
              byte[] bytes = classWriter.toByteArray();
              Class generatedClass = instance.defineClass(name, bytes, 0, bytes.length);
      
              if(new File(name + ".class").exists())
                  new File(name + ".class").delete();
              FileOutputStream fos = new FileOutputStream(name + ".class");
              fos.write(bytes);
              fos.close();
          }
      }
      

      On lance pour voir:

      Ça marche!

      Petite explication du stack

      🤔 Le stack, qu’est-ce que c’est?
      ❗ Le stack est une grosse pile comportant des valeurs empilées les unes sur les autres avec le fait que l’on puisse que rajouter une valeur au dessus du stack ou en retirer une, sur le même principe de ces jeux pour enfants:
      0_1529786862247_HTB1tIIeGXXXXXa.XpXXq6xXFXXXe.jpg (crédit photo: aliexpress.com)

      🤔 Ouais, mais ça sert à quoi?
      ❗ Le stack est ce que la JVM utilise pour gérer les valeurs stockées en mémoire, voici une liste des principaux types que l’on peut retrouver:

      • arrayref: un tableau
      • null: la valeur null
      • value: une valeur quelconque, son type dépend de l’instruction
      • objectref: un objet
      • address: une adresse mémoire

      En ajoutant et en retirant des valeurs sur ce stack, on peut effectuer des calculs, des appels de méthodes, des sauts, etc.
      Il est important de noter qu’une fois qu’une valeur est utilisée dans le stack, il y en est retirée.

      Charger un primitif

      Pour charger un primitif (pour la JVM, c’est byte, int, float, double, long ou String) sur le stack, on utilise l’opcode LDC, représenter par la méthode visitLdcInsn(Object o) où o représente l’objet à charger sur le stack.
      Exemples:

      mv.visitLdcInsn(1); // charge 1 int, voire byte sur le stack
      
      mv.visitLdcInsn(50.56); // charge 50.56 double sur le stack
      
      mv.visitLdcInsn(3.14f); // charge 3.14f float sur le stack
      
      mv.visitLdcInsn("Hello world"); // charge "Hello world" (String) sur le stack
      

      Et c’est tout!

      Sauvegarder dans des variables locales

      Pour sauvegarder dans une variable locale, on utilise les opcodes du type tSTORE où t est le type de la variable locale:

      • L: long
      • D: double
      • F: float
      • I: int
      • A: le reste des objets

      Cet opcode est suivi par l’index de la variable à sauvegarder. Avec ASM, on utilise la méthode de MethodVisitor:

      public void visitVarInsn(int opcode, int index)
      
      • opcode: l’opcode à utiliser (exemple: ALOAD, ISTORE, etc.)
      • index: l’index de la variable à utiliser (exemple: 0, 1, 14, etc.)

      Pour démontrer ceci, nous allons créer une nouvelle variable locale, après

      mv.visitLocalVariable("args", Type.getDescriptor(String[].class), null, new Label(), new Label(), 0);
      

      Nous allons insérer ceci pour créer cette nouvelle variable locale:

      mv.visitLocalVariable("intExemple", Type.INT_TYPE.getDescriptor(), null, new Label(), new Label(), 1);
      

      Equivalent de:

      int intExemple;
      

      Et nous allons stocker 42 dedans: on charge 42 sur le stack puis on stocke la valeur à l’aide de ISTORE. Si vous avez suivi la partie précédente, pour charger 42, on va utiliser

      void visitLdcInsn(Object o)
      

      Ainsi, on insère ceci dans le code:

      mv.visitLdcInsn(42); // charge 42 sur le stack
      mv.visitVarInsn(ISTORE, 1); // prend la première valeur du stack et la sauvegarde dans la variable locale 1
      

      On lance et…

      🤔 Mais pourquoi c’est un byte?
      ❗ Ne t’inquiète pas, cela vient de mon décompileur (Fernflower) qui va choisir le type avec la taille la plus appropriée au contenu de la variable. JD-GUI, quant à lui, nous indique bien un int:

      Charger une variable locale sur le stack

      TODO

      TODO:
      Code couleur
      Ajouter des trucs

      Modérateur sur MFF. 
      Mon Github: http://github.com/jglrxavpok
      Mon compte Steam si vous voulez jouer à CS:GO ou TF2 avec moi: https://steamcommunity.com/id/jglrxavpok/

      1 réponse Dernière réponse Répondre Citer 0
      • Gugu
        Gugu dernière édition par robin4002

        "If you have a comprehensive explanation for everything then it decreases uncertainty and anxiety and reduces your cognitive load. And if you can use that simplifying algorithm to put yourself on the side of moral virtue then you’re constantly a good person with a minimum of effort."
        ― Jordan B. Peterson

        1 réponse Dernière réponse Répondre Citer 0
        • Algorythmis
          Algorythmis dernière édition par robin4002

          @‘Gugu42’:

          Mes mods :

          • Cookie Ore (1.7.10)

          Mod en cours : **E…

          1 réponse Dernière réponse Répondre Citer 0
          • isador
            isador Moddeurs confirmés Modérateurs dernière édition par robin4002

            Bravo xavpok un bon tuto en prévision;

            1 réponse Dernière réponse Répondre Citer 0
            • Snyker
              Snyker dernière édition par robin4002

              @‘Gugu42’:

              Il me semble tu doit le finir nan ?

              Aucune signature n'est disponible pour une barre chocolatée.

              1 réponse Dernière réponse Répondre Citer 0
              • elias54
                elias54 Administrateurs dernière édition par robin4002

                Mon site | GitHub

                1 réponse Dernière réponse Répondre Citer 0
                • jglrxavpok
                  jglrxavpok Modérateurs dernière édition par

                  Oh mon dieu, je l’ai oublié totalement ce tuto! =-O=-O
                  Je vais essayer de le continuer ^^’

                  Sent from my GT-I9000 using Tapatalk 2

                  Modérateur sur MFF. 
                  Mon Github: http://github.com/jglrxavpok
                  Mon compte Steam si vous voulez jouer à CS:GO ou TF2 avec moi: https://steamcommunity.com/id/jglrxavpok/

                  1 réponse Dernière réponse Répondre Citer 0
                  • Snyker
                    Snyker dernière édition par

                    Heureusement que je suis là xD

                    Aucune signature n'est disponible pour une barre chocolatée.

                    1 réponse Dernière réponse Répondre Citer 0
                    • jglrxavpok
                      jglrxavpok Modérateurs dernière édition par

                      Oui merci ^^

                      oublies de nouveau l’existance même de ce tuto

                      Sent from my GT-I9000 using Tapatalk 2

                      Modérateur sur MFF. 
                      Mon Github: http://github.com/jglrxavpok
                      Mon compte Steam si vous voulez jouer à CS:GO ou TF2 avec moi: https://steamcommunity.com/id/jglrxavpok/

                      1 réponse Dernière réponse Répondre Citer 0
                      • jglrxavpok
                        jglrxavpok Modérateurs dernière édition par

                        Alors, bonne nouvelle!
                        J’ai créé un langage tournant sur la JVM avec ASM, donc je vais avoir plein plein de contenu pour continuer le tuto!

                        PS: Désolé robin-senpai pour le double post, ne me frappez pas :c

                        Sent from my GT-I9000 using Tapatalk 2

                        Modérateur sur MFF. 
                        Mon Github: http://github.com/jglrxavpok
                        Mon compte Steam si vous voulez jouer à CS:GO ou TF2 avec moi: https://steamcommunity.com/id/jglrxavpok/

                        1 réponse Dernière réponse Répondre Citer 0
                        • robin4002
                          robin4002 Moddeurs confirmés Rédacteurs Administrateurs dernière édition par

                          +24h entre deux posts = pas de problème.

                          1 réponse Dernière réponse Répondre Citer 0
                          • Flow Arg
                            Flow Arg Moddeurs confirmés dernière édition par

                            Super tutoriel, encore d’actualité de nos jours !
                            voici une petite coquille 5a044f8c-6ef4-451b-8785-4c234f37e4af-image.png
                            et question a part, il y a moyen de faire de l’ASM mais uniquement au runtime ? C’est à dire que ça ne fait les modifications uniquement pendant l’exécution du programme, quand le programme est éteint, les modifications ne sont pas visible.

                            Mon GitHub
                            Mon repo Maven
                            Mon Updater
                            Je suis un membre apprécié et joueur, j'ai déjà obtenu 10 points de réputation.

                            1 réponse Dernière réponse Répondre Citer 0
                            • robin4002
                              robin4002 Moddeurs confirmés Rédacteurs Administrateurs dernière édition par robin4002

                              J’ai corrigé le formatage du post, merci du signalement 😉

                              1 réponse Dernière réponse Répondre Citer 0
                              • Référencé par  SCAREX SCAREX 
                              • 1 / 1
                              • Premier message
                                Dernier message
                              Design by Woryk
                              Contact / Mentions Légales

                              MINECRAFT FORGE FRANCE © 2018

                              Powered by NodeBB