Créer un java.lang.Ssortingng mutable

Tout le monde sait que les Java Ssortingng sont immuables. Les cordes immuables sont un excellent ajout à Java depuis sa création. Immutabilité permet un access rapide et beaucoup d’optimisations, significativement moins sujettes aux erreurs par rapport aux chaînes de style C, et aide à appliquer le modèle de sécurité.

Il est possible de créer un mutable sans utiliser de hacks, à savoir

  • java.lang.reflect
  • sun.misc.Unsafe
  • Classes dans classloader bootstrap
  • JNI (ou JNA car il nécessite JNI)

Mais est-ce possible en Java, pour que la chaîne puisse être modifiée à tout moment? La question est comment ?

En créant un java.lang.Ssortingng avec le constructeur Charset, on peut injecter son propre Charset, qui apporte son propre CharsetDecoder . Le CharsetDecoder obtient une référence à un object CharBuffer dans la méthode decodeLoop. Le CharBuffer encapsule le caractère [] de l’object Ssortingng d’origine. Comme le CharsetDecoder y fait référence, vous pouvez changer le caractère sous-jacent [] en utilisant le CharBuffer, vous avez donc une chaîne modifiable.

 public class MutableSsortingngTest { // http://stackoverflow.com/questions/11146255/how-to-create-mutable-java-lang-ssortingng#11146288 @Test public void testMutableSsortingng() throws Exception { final Ssortingng s = createModifiableSsortingng(); System.out.println(s); modify(s); System.out.println(s); } private final AtomicReference cbRef = new AtomicReference(); private Ssortingng createModifiableSsortingng() { Charset charset = new Charset("foo", null) { @Override public boolean contains(Charset cs) { return false; } @Override public CharsetDecoder newDecoder() { CharsetDecoder cd = new CharsetDecoder(this, 1.0f, 1.0f) { @Override protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { cbRef.set(out); while(in.remaining()>0) { out.append((char)in.get()); } return CoderResult.UNDERFLOW; } }; return cd; } @Override public CharsetEncoder newEncoder() { return null; } }; return new Ssortingng("abc".getBytes(), charset); } private void modify(Ssortingng s) { CharBuffer charBuffer = cbRef.get(); charBuffer.position(0); charBuffer.put("xyz"); } } 

Lancer le code imprime

 abc zzz 

Je ne sais pas comment implémenter correctement decodeLoop (), mais je m’en fiche en ce moment 🙂

La question a reçu une bonne réponse de @mhaller. Je dirais que le soi-disant casse-tête était assez facile et en regardant simplement les cœurs disponibles de Ssortingng, on devrait être en mesure de trouver comment , un

Procédure pas à pas

Vous trouverez ci-dessous un intérêt particulier: si vous voulez pénétrer / craquer / rechercher des vulnérabilités de sécurité, recherchez toujours des classes arbitraires non finales. Le cas ici est java.nio.charset.Charset

 //Ssortingng public Ssortingng(byte bytes[], int offset, int length, Charset charset) { if (charset == null) throw new NullPointerException("charset"); checkBounds(bytes, offset, length); char[] v = SsortingngCoding.decode(charset, bytes, offset, length); this.offset = 0; this.count = v.length; this.value = v; } 

Le c-tor offre un moyen supposément rapide de convertir l’ byte[] en Ssortingng en passant le Charset pas le nom du jeu de cartes pour éviter le lookset chartsetName-> charset. Il permet également de passer un object Charset arbitraire pour créer Ssortingng. Le routage principal du jeu de caractères convertit le contenu de java.nio.ByteBuffer en CharBuffer . Le CharBuffer peut contenir une référence à char [] et il est disponible via array() , également le CharBuffer est entièrement modifiable.

 //SsortingngCoding static char[] decode(Charset cs, byte[] ba, int off, int len) { SsortingngDecoder sd = new SsortingngDecoder(cs, cs.name()); byte[] b = Arrays.copyOf(ba, ba.length); return sd.decode(b, off, len); } //SsortingngDecoder char[] decode(byte[] ba, int off, int len) { int en = scale(len, cd.maxCharsPerByte()); char[] ca = new char[en]; if (len == 0) return ca; cd.reset(); ByteBuffer bb = ByteBuffer.wrap(ba, off, len); CharBuffer cb = CharBuffer.wrap(ca); try { CoderResult cr = cd.decode(bb, cb, true); if (!cr.isUnderflow()) cr.throwException(); cr = cd.flush(cb); if (!cr.isUnderflow()) cr.throwException(); } catch (CharacterCodingException x) { // Substitution is always enabled, // so this shouldn't happen throw new Error(x); } return safeTrim(ca, cb.position(), cs); } 

Afin d’éviter de modifier le caractère char[] les développeurs Java copient le tableau comme n’importe quelle autre construction Ssortingng (par exemple public Ssortingng(char value[]) ). Cependant, il existe une exception: si aucun SecurityManager n’est installé, le caractère [] n’est pas copié.

     // Coupe le tableau de caractères donné à la longueur donnée
     //
     char statique privé [] safeTrim (char [] ca, int len, Charset cs) {
         si (len == ca.length 
                 && (System.getSecurityManager () == null)
                 ||  cs.getClass (). getClassLoader0 () == null))
             retour ca
         autre
             retourner Arrays.copyOf (ca, len);
     }

Donc, s’il n’y a pas de SecurityManager, il est absolument possible d’avoir un CharBuffer / char [] modifiable qui soit référencé par une Ssortingng.

Tout va bien maintenant – sauf que l’ byte[] est également copié (le gras ci-dessus). C’est là que les développeurs de Java sont devenus paresseux et complètement faux.

La copie est nécessaire pour empêcher le Charset Rogue (exemple ci-dessus) de pouvoir modifier l’octet source []. Cependant, imaginez le cas d’avoir environ 512 Ko d’ byte[] contenant peu de chaînes. Tentative de création d’un petit, peu de graphiques – new Ssortingng(buf, position, position+32,charset) résultant en une copie massive de 512 Ko byte. Si le tampon était d’environ 1 Ko, l’impact ne sera jamais vraiment remarqué. Avec de gros tampons, la performance est vraiment énorme. La solution simple serait de copier la partie concernée.

… ou bien les concepteurs de java.nio réfléchi en introduisant des tampons en lecture seule. Le simple fait d’appeler ByteBuffer.asReadOnlyBuffer() aurait suffi (si le Charset.getClassLoader ()! = Null) * Parfois, même les gars travaillant sur java.lang peuvent se tromper complètement.

* Class.getClassLoader () renvoie null pour les classes d’amorçage, c’est-à-dire celles fournies avec la JVM elle-même.

Je dirais SsortingngBuilder (ou SsortingngBuffer pour une utilisation multithread). Oui, à la fin, vous obtenez une chaîne immuable. Mais c’est la voie à suivre.

Par exemple, la meilleure façon d’append des chaînes dans une boucle consiste à utiliser SsortingngBuilder. Java lui-même utilise SsortingngBuilder lorsque vous utilisez “fu” + variable + “ba”.

http://docs.oracle.com/javase/6/docs/api/java/lang/SsortingngBuilder.html

append (blub) .append (5) .appen (“dfgdfg”). toSsortingng ();

 // How to achieve Ssortingng Mutability import java.lang.reflect.Field; public class MutableSsortingng { public static void main(Ssortingng[] args) { Ssortingng s = "Hello"; mutate(s); System.out.println(s); } public static void mutate(Ssortingng s) { try { Ssortingng t = "Hello world"; Field val = Ssortingng.class.getDeclaredField("value"); Field count = Ssortingng.class.getDeclaredField("count"); val.setAccessible(true); count.setAccessible(true); count.setInt (s, t.length ()); val.set (s, val.get(t)); } catch (Exception e) { e.printStackTrace(); } } } 

Ne réinventez pas la roue. Apache commons ne fournit que cela.

 MutableObject mutableSsortingng = new new MutableObject<>(); 

Un moyen plus simple d’échanger le chemin de classe bootstrap de java et javac

1) Accédez à l’installation de jdk et copiez-le dans le dossier séparé rt.jar et src.zip

2) Décompressez Ssortingng.java à partir des sources zip et changez la valeur du champ privé du tableau interne en public

 public final class Ssortingng implements java.io.Serializable, Comparable, CharSequence { /** The value is used for character storage. */ public final char value[]; 

3) Comstackz Ssortingng.java modifié avec l’aide de javac:

 javac Ssortingng.java 

4) Déplacez compilé Ssortingng.class et autres classes compilées vers rt.jar dans ce répertoire

5) Créer une classe de test utilisant le champ privé Ssortingng

 package exp; class MutableSsortingngExp { public static void main(Ssortingng[] args) { Ssortingng letter = "A"; System.out.println(letter); letter.value[0] = 'X'; System.out.println(letter); } } 

6) Créer une target répertoire vide et comstackr une classe de test

 javac -Xbootclasspath:rt.jar -d target MutableSsortingngExp.java 

7) Exécuter

 java -Xbootclasspath:rt.jar -cp "target" exp.MutableSsortingngExp 

la sortie est:

 A X 

PS cela ne fonctionnera qu’avec rt.jar modifié et utiliser cette option pour remplacer rt.jar est une violation de licence jre .