diff --git a/nondex-core/src/main/java/java/util/HashMap.java b/nondex-core/src/main/java/java/util/HashMap.java index 3b3d995b..5f8efc82 100644 --- a/nondex-core/src/main/java/java/util/HashMap.java +++ b/nondex-core/src/main/java/java/util/HashMap.java @@ -1517,9 +1517,13 @@ public final long estimateSize() { static final class KeySpliterator extends HashMapSpliterator implements Spliterator { + + private final KeySpliteratorShuffler shuffler; + KeySpliterator(HashMap m, int origin, int fence, int est, int expectedModCount) { super(m, origin, fence, est, expectedModCount); + this.shuffler = new KeySpliteratorShuffler<>(this); } public KeySpliterator trySplit() { @@ -1530,6 +1534,10 @@ public KeySpliterator trySplit() { } public void forEachRemaining(Consumer action) { + shuffler.forEachRemaining(action); + } + + public void original_forEachRemaining(Consumer action) { int i, hi, mc; if (action == null) throw new NullPointerException(); @@ -1559,6 +1567,10 @@ public void forEachRemaining(Consumer action) { } public boolean tryAdvance(Consumer action) { + return shuffler.tryAdvance(action); + } + + public boolean original_tryAdvance(Consumer action) { int hi; if (action == null) throw new NullPointerException(); diff --git a/nondex-core/src/main/java/java/util/KeySpliteratorShuffler.java b/nondex-core/src/main/java/java/util/KeySpliteratorShuffler.java new file mode 100644 index 00000000..5ce548b2 --- /dev/null +++ b/nondex-core/src/main/java/java/util/KeySpliteratorShuffler.java @@ -0,0 +1,34 @@ +package java.util; +import java.util.function.Consumer; +import java.util.HashMap.KeySpliterator; + +public class KeySpliteratorShuffler { + + private final Iterator iter; + private final KeySpliterator keySpliterator; + + public KeySpliteratorShuffler(KeySpliterator ks) { + this.keySpliterator = ks; + + final List keys = new ArrayList<>(); + keySpliterator.original_forEachRemaining(key -> keys.add(key)); + + List res = edu.illinois.nondex.shuffling.ControlNondeterminism.shuffle(keys); + + iter = res.iterator(); + } + + public void forEachRemaining(Consumer action) { + iter.forEachRemaining(action); + } + + public boolean tryAdvance(Consumer action) { + if (iter.hasNext()) { + K nextElement = iter.next(); + action.accept(nextElement); + return true; + } + return false; + } + +} diff --git a/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/CVFactory.java b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/CVFactory.java index 36b027fa..408b9809 100644 --- a/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/CVFactory.java +++ b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/CVFactory.java @@ -47,6 +47,8 @@ public static ClassVisitor construct(ClassVisitor cv, String clzToInstrument) } else if (Instrumenter.hasClassEntry(Instrumenter.hashMapEntryName)) { return new HashMapShufflingAdder(cv, "Entry"); } + } else if (clzToInstrument.equals(Instrumenter.keySpliteratorName)) { + return new HashMapKeySplitAdder(cv); } else if (clzToInstrument.equals(Instrumenter.weakHashMapName)) { return new WeakHashMapShufflingAdder(cv); } else if (clzToInstrument.equals(Instrumenter.identityHashMapName)) { diff --git a/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashKeySpliteratorASMDump.java b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashKeySpliteratorASMDump.java new file mode 100644 index 00000000..52583bca --- /dev/null +++ b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashKeySpliteratorASMDump.java @@ -0,0 +1,176 @@ +package edu.illinois.nondex.instr; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.ConstantDynamic; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.RecordComponentVisitor; +import org.objectweb.asm.Type; +import org.objectweb.asm.TypePath; + +public class HashKeySpliteratorASMDump implements Opcodes { + + public static byte[] dump() { + + ClassWriter classWriter = new ClassWriter(0); + FieldVisitor fieldVisitor; + MethodVisitor methodVisitor; + + classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", "Ljava/lang/Object;", "java/lang/Object", null); + + classWriter.visitSource("HashMap$KeySpliterator$KeySpliteratorShuffler.java", null); + + classWriter.visitInnerClass("java/util/HashMap$KeySpliterator", "java/util/HashMap", "KeySpliterator", ACC_FINAL | ACC_STATIC); + + classWriter.visitInnerClass("java/lang/invoke/MethodHandles$Lookup", "java/lang/invoke/MethodHandles", "Lookup", ACC_PUBLIC | ACC_FINAL | ACC_STATIC); + + { + fieldVisitor = classWriter.visitField(ACC_PRIVATE | ACC_FINAL, "iter", "Ljava/util/Iterator;", "Ljava/util/Iterator;", null); + fieldVisitor.visitEnd(); + } + { + fieldVisitor = classWriter.visitField(ACC_PRIVATE | ACC_FINAL, "keySpliterator", "Ljava/util/HashMap$KeySpliterator;", "Ljava/util/HashMap$KeySpliterator;", null); + fieldVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "", "(Ljava/util/HashMap$KeySpliterator;)V", "(Ljava/util/HashMap$KeySpliterator;)V", null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(12, label0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(13, label1); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitFieldInsn(PUTFIELD, "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", "keySpliterator", "Ljava/util/HashMap$KeySpliterator;"); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLineNumber(16, label2); + methodVisitor.visitTypeInsn(NEW, "java/util/ArrayList"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "", "()V", false); + methodVisitor.visitVarInsn(ASTORE, 2); + Label label3 = new Label(); + methodVisitor.visitLabel(label3); + methodVisitor.visitLineNumber(17, label3); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitFieldInsn(GETFIELD, "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", "keySpliterator", "Ljava/util/HashMap$KeySpliterator;"); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitInvokeDynamicInsn("accept", "(Ljava/util/List;)Ljava/util/function/Consumer;", new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), new Object[]{Type.getType("(Ljava/lang/Object;)V"), new Handle(Opcodes.H_INVOKESTATIC, "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", "lambda$new$0", "(Ljava/util/List;Ljava/lang/Object;)V", false), Type.getType("(Ljava/lang/Object;)V")}); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap$KeySpliterator", "original_forEachRemaining", "(Ljava/util/function/Consumer;)V", false); + Label label4 = new Label(); + methodVisitor.visitLabel(label4); + methodVisitor.visitLineNumber(20, label4); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKESTATIC, "edu/illinois/nondex/shuffling/ControlNondeterminism", "shuffle", "(Ljava/util/List;)Ljava/util/List;", false); + methodVisitor.visitVarInsn(ASTORE, 3); + Label label5 = new Label(); + methodVisitor.visitLabel(label5); + methodVisitor.visitLineNumber(23, label5); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitVarInsn(ALOAD, 3); + methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;", true); + methodVisitor.visitFieldInsn(PUTFIELD, "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", "iter", "Ljava/util/Iterator;"); + Label label6 = new Label(); + methodVisitor.visitLabel(label6); + methodVisitor.visitLineNumber(24, label6); + methodVisitor.visitInsn(RETURN); + Label label7 = new Label(); + methodVisitor.visitLabel(label7); + methodVisitor.visitLocalVariable("this", "Ljava/util/HashMap$KeySpliterator$KeySpliteratorShuffler;", "Ljava/util/HashMap$KeySpliterator$KeySpliteratorShuffler;", label0, label7, 0); + methodVisitor.visitLocalVariable("ks", "Ljava/util/HashMap$KeySpliterator;", "Ljava/util/HashMap$KeySpliterator;", label0, label7, 1); + methodVisitor.visitLocalVariable("keys", "Ljava/util/List;", "Ljava/util/List;", label3, label7, 2); + methodVisitor.visitLocalVariable("res", "Ljava/util/List;", "Ljava/util/List;", label5, label7, 3); + methodVisitor.visitMaxs(2, 4); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "forEachRemaining", "(Ljava/util/function/Consumer;)V", "(Ljava/util/function/Consumer<-TK;>;)V", null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(31, label0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitFieldInsn(GETFIELD, "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", "iter", "Ljava/util/Iterator;"); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "forEachRemaining", "(Ljava/util/function/Consumer;)V", true); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(32, label1); + methodVisitor.visitInsn(RETURN); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLocalVariable("this", "Ljava/util/HashMap$KeySpliterator$KeySpliteratorShuffler;", "Ljava/util/HashMap$KeySpliterator$KeySpliteratorShuffler;", label0, label2, 0); + methodVisitor.visitLocalVariable("action", "Ljava/util/function/Consumer;", "Ljava/util/function/Consumer<-TK;>;", label0, label2, 1); + methodVisitor.visitMaxs(2, 2); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "tryAdvance", "(Ljava/util/function/Consumer;)Z", "(Ljava/util/function/Consumer<-TK;>;)Z", null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitFieldInsn(GETFIELD, "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", "iter", "Ljava/util/Iterator;"); + methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z", true); + Label label1 = new Label(); + methodVisitor.visitJumpInsn(IFEQ, label1); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitFieldInsn(GETFIELD, "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", "iter", "Ljava/util/Iterator;"); + methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;", true); + methodVisitor.visitVarInsn(ASTORE, 2); + Label label3 = new Label(); + methodVisitor.visitLabel(label3); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/function/Consumer", "accept", "(Ljava/lang/Object;)V", true); + Label label4 = new Label(); + methodVisitor.visitLabel(label4); + methodVisitor.visitInsn(ICONST_1); + methodVisitor.visitInsn(IRETURN); + methodVisitor.visitLabel(label1); + methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + methodVisitor.visitInsn(ICONST_0); + methodVisitor.visitInsn(IRETURN); + Label label5 = new Label(); + methodVisitor.visitLabel(label5); + methodVisitor.visitLocalVariable("nextElement", "Ljava/lang/Object;", "TK;", label3, label1, 2); + methodVisitor.visitLocalVariable("this", "Ljava/util/HashMap$KeySpliterator$KeySpliteratorShuffler;", "Ljava/util/HashMap$KeySpliterator$KeySpliteratorShuffler;", label0, label5, 0); + methodVisitor.visitLocalVariable("action", "Ljava/util/function/Consumer;", "Ljava/util/function/Consumer<-TK;>;", label0, label5, 1); + methodVisitor.visitMaxs(2, 3); + methodVisitor.visitEnd(); + } + { + methodVisitor = classWriter.visitMethod(ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, "lambda$new$0", "(Ljava/util/List;Ljava/lang/Object;)V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(17, label0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true); + methodVisitor.visitInsn(POP); + methodVisitor.visitInsn(RETURN); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLocalVariable("keys", "Ljava/util/List;", null, label0, label1, 0); + methodVisitor.visitLocalVariable("key", "Ljava/lang/Object;", null, label0, label1, 1); + methodVisitor.visitMaxs(2, 2); + methodVisitor.visitEnd(); + } + classWriter.visitEnd(); + + return classWriter.toByteArray(); + } +} diff --git a/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashMapKeySplitAdder.java b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashMapKeySplitAdder.java new file mode 100644 index 00000000..770a00c9 --- /dev/null +++ b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashMapKeySplitAdder.java @@ -0,0 +1,213 @@ +/* +The MIT License (MIT) +Copyright (c) 2015 Alex Gyori +Copyright (c) 2022 Kaiyao Ke +Copyright (c) 2015 Owolabi Legunsen +Copyright (c) 2015 Darko Marinov +Copyright (c) 2015 August Shi + + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +package edu.illinois.nondex.instr; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class HashMapKeySplitAdder extends ClassVisitor { + + public HashMapKeySplitAdder(ClassVisitor ca) { + super(Opcodes.ASM9, ca); + } + + public void addSplitShufflerField() { + FieldVisitor fv = super.visitField( + Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, + "shuffler", + "Ljava/util/HashMap$KeySpliterator$KeySpliteratorShuffler;", + "Ljava/util/HashMap.KeySpliterator.KeySpliteratorShuffler;", + null + ); + fv.visitEnd(); + } + + public void addForEachRemaining() { + MethodVisitor methodVisitor = super.visitMethod( + Opcodes.ACC_PUBLIC, + "forEachRemaining", + "(Ljava/util/function/Consumer;)V", + "(Ljava/util/function/Consumer<-TK;>;)V", + null + ); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); + methodVisitor.visitFieldInsn( + Opcodes.GETFIELD, + "java/util/HashMap$KeySpliterator", + "shuffler", + "Ljava/util/HashMap$KeySpliterator$KeySpliteratorShuffler;" + ); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 1); + methodVisitor.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", + "forEachRemaining", + "(Ljava/util/function/Consumer;)V", + false + ); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitInsn(Opcodes.RETURN); + Label label2 = new Label(); + methodVisitor.visitLabel(label2); + methodVisitor.visitLocalVariable( + "this", + "Ljava/util/HashMap$KeySpliterator;", + "Ljava/util/HashMap$KeySpliterator;", + label0, + label2, + 0 + ); + methodVisitor.visitLocalVariable( + "action", + "Ljava/util/function/Consumer;", + "Ljava/util/function/Consumer<-TK;>;", + label0, + label2, + 1 + ); + methodVisitor.visitMaxs(2, 2); + methodVisitor.visitEnd(); + } + + public void addTryAdvance() { + MethodVisitor methodVisitor = super.visitMethod( + Opcodes.ACC_PUBLIC, + "tryAdvance", + "(Ljava/util/function/Consumer;)Z", + "(Ljava/util/function/Consumer<-TK;>;)Z", + null + ); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); + methodVisitor.visitFieldInsn( + Opcodes.GETFIELD, + "java/util/HashMap$KeySpliterator", + "shuffler", + "Ljava/util/HashMap$KeySpliterator$KeySpliteratorShuffler;" + ); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 1); + methodVisitor.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", + "tryAdvance", + "(Ljava/util/function/Consumer;)Z", + false + ); + methodVisitor.visitInsn(Opcodes.IRETURN); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLocalVariable( + "this", + "Ljava/util/HashMap$KeySpliterator;", + "Ljava/util/HashMap$KeySpliterator;", + label0, + label1, + 0 + ); + methodVisitor.visitLocalVariable( + "action", + "Ljava/util/function/Consumer;", + "Ljava/util/function/Consumer<-TK;>;", + label0, + label1, + 1 + ); + methodVisitor.visitMaxs(2, 2); + methodVisitor.visitEnd(); + } + + @Override + public void visitEnd() { + + addSplitShufflerField(); + addForEachRemaining(); + addTryAdvance(); + + super.visitInnerClass( + "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", + "java/util/HashMap$KeySpliterator", + "KeySpliteratorShuffler", + 0 + ); + super.visitEnd(); + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions + ) { + if ("".equals(name)) { + return new MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, desc, signature, exceptions)) { + @Override + public void visitInsn(int opcode) { + if (opcode == Opcodes.RETURN) { + super.visitVarInsn(Opcodes.ALOAD, 0); + super.visitTypeInsn( + Opcodes.NEW, + "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler" + ); + super.visitInsn(Opcodes.DUP); + super.visitVarInsn(Opcodes.ALOAD, 0); + super.visitMethodInsn( + Opcodes.INVOKESPECIAL, + "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", + "", + "(Ljava/util/HashMap$KeySpliterator;)V", + false + ); + super.visitFieldInsn( + Opcodes.PUTFIELD, + "java/util/HashMap$KeySpliterator", + "shuffler", + "Ljava/util/HashMap$KeySpliterator$KeySpliteratorShuffler;" + ); + } + super.visitInsn(opcode); + } + }; + } + if ("forEachRemaining".equals(name)) { + // Rename the existing method to original_forEachRemaining + return super.visitMethod(access, "original_forEachRemaining", desc, signature, exceptions); + } + if ("tryAdvance".equals(name)) { + return super.visitMethod(access, "original_tryAdvance", desc, signature, exceptions); + } + return super.visitMethod(access, name, desc, signature, exceptions); + } +} diff --git a/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/Instrumenter.java b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/Instrumenter.java index ac6a07be..3a602a9d 100644 --- a/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/Instrumenter.java +++ b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/Instrumenter.java @@ -62,6 +62,7 @@ a copy of this software and associated documentation files (the public final class Instrumenter { public static final String hashMapName = "java/util/HashMap$HashIterator.class"; + public static final String keySpliteratorName = "java/util/HashMap$KeySpliterator.class"; public static final String weakHashMapName = "java/util/WeakHashMap$HashIterator.class"; public static final String identityHashMapName = "java/util/IdentityHashMap$IdentityHashMapIterator.class"; public static final String concurrentHashMapName = "java/util/concurrent/ConcurrentHashMap$Traverser.class"; @@ -72,6 +73,7 @@ public final class Instrumenter { public static final String hashMapNodeName = "java/util/HashMap$Node.class"; public static final String hashMapEntryName = "java/util/HashMap$Entry.class"; public static final String hashMapHashIteratorShufflerName = "java/util/HashMap$HashIterator$HashIteratorShuffler.class"; + public static final String hashMapKeySpliShufflerName = "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler.class"; private static final String rootPath = "modules/java.base"; @@ -97,6 +99,7 @@ private Instrumenter() { this.standardClassesToInstrument.add("java/util/PriorityQueue.class"); this.specialClassesToInstrument.add(Instrumenter.hashMapName); + this.specialClassesToInstrument.add(Instrumenter.keySpliteratorName); this.specialClassesToInstrument.add(Instrumenter.weakHashMapName); this.specialClassesToInstrument.add(Instrumenter.identityHashMapName); this.specialClassesToInstrument.add(Instrumenter.concurrentHashMapName); @@ -203,6 +206,13 @@ public byte[] apply() { } }); } + // add SpliteratorShuffler.class + this.addAsmDumpResultToZip(outZip, hashMapKeySpliShufflerName, new Producer() { + @Override + public byte[] apply() { + return HashKeySpliteratorASMDump.dump(); + } + }); for (String clz : this.specialClassesToInstrument) { this.instrumentSpecialClass(outZip, clz);