From b1276750b2a79a3f272374f871661cd5664dd575 Mon Sep 17 00:00:00 2001 From: Yusen Wang Date: Tue, 19 Nov 2024 17:18:43 -0600 Subject: [PATCH 1/3] Add temporary workaround for shuffling HashMap KeySpliterator --- .../src/main/java/java/util/HashMap.java | 8 + .../java/util/KeySpliteratorShuffler.java | 24 +++ .../edu/illinois/nondex/instr/CVFactory.java | 2 + .../instr/HashKeySpliteratorASMDump.java | 155 ++++++++++++++++++ .../nondex/instr/HashMapKeySplitAdder.java | 75 +++++++++ .../illinois/nondex/instr/Instrumenter.java | 11 ++ 6 files changed, 275 insertions(+) create mode 100644 nondex-core/src/main/java/java/util/KeySpliteratorShuffler.java create mode 100644 nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashKeySpliteratorASMDump.java create mode 100644 nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashMapKeySplitAdder.java diff --git a/nondex-core/src/main/java/java/util/HashMap.java b/nondex-core/src/main/java/java/util/HashMap.java index 3b3d995b..3d3e6dc2 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(); 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..1baa0963 --- /dev/null +++ b/nondex-core/src/main/java/java/util/KeySpliteratorShuffler.java @@ -0,0 +1,24 @@ +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); + } +} \ No newline at end of file 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..56570401 --- /dev/null +++ b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashKeySpliteratorASMDump.java @@ -0,0 +1,155 @@ +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_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(); + } + { + methodVisitor = classWriter.visitMethod(ACC_STATIC, "", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(7, label0); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn("YW=============shuffling ============"); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(8, label1); + methodVisitor.visitInsn(RETURN); + methodVisitor.visitMaxs(2, 0); + 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..b5e82ce0 --- /dev/null +++ b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashMapKeySplitAdder.java @@ -0,0 +1,75 @@ +package edu.illinois.nondex.instr; + +import org.objectweb.asm.*; + +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(); + } + + @Override + public void visitEnd() { + addSplitShufflerField(); + addForEachRemaining(); + 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); // Push "this" (KeySpliterator) + super.visitTypeInsn(Opcodes.NEW, "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler"); // Create new KeySpliteratorShuffler + super.visitInsn(Opcodes.DUP); // Duplicate the reference to the new object + super.visitVarInsn(Opcodes.ALOAD, 0); // Push "this" again for the constructor + super.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", + "", "(Ljava/util/HashMap$KeySpliterator;)V", false); // Call constructor + super.visitFieldInsn(Opcodes.PUTFIELD, "java/util/HashMap$KeySpliterator", "shuffler", + "Ljava/util/HashMap$KeySpliterator$KeySpliteratorShuffler;"); // Assign to shuffler + } + super.visitInsn(opcode); + } + }; + } + if ("forEachRemaining".equals(name)) { + // Rename the existing method to original_forEachRemaining + return super.visitMethod(access, "original_forEachRemaining", 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..5181fa40 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 hashMapKeySpliteratorShufflerName = "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,14 @@ public byte[] apply() { } }); } + + // add SpliteratorShuffler.class + this.addAsmDumpResultToZip(outZip, hashMapKeySpliteratorShufflerName, new Producer() { + @Override + public byte[] apply() { + return HashKeySpliteratorASMDump.dump(); + } + }); for (String clz : this.specialClassesToInstrument) { this.instrumentSpecialClass(outZip, clz); From cde79dccf50a427d2c1c96591a1ca64c7fe42a78 Mon Sep 17 00:00:00 2001 From: Yusen Wang Date: Thu, 21 Nov 2024 11:29:05 -0600 Subject: [PATCH 2/3] resolved checkstyle --- .../java/util/KeySpliteratorShuffler.java | 2 +- .../instr/HashKeySpliteratorASMDump.java | 17 --- .../nondex/instr/HashMapKeySplitAdder.java | 117 +++++++++++++++--- .../illinois/nondex/instr/Instrumenter.java | 5 +- 4 files changed, 103 insertions(+), 38 deletions(-) diff --git a/nondex-core/src/main/java/java/util/KeySpliteratorShuffler.java b/nondex-core/src/main/java/java/util/KeySpliteratorShuffler.java index 1baa0963..fadb1df1 100644 --- a/nondex-core/src/main/java/java/util/KeySpliteratorShuffler.java +++ b/nondex-core/src/main/java/java/util/KeySpliteratorShuffler.java @@ -21,4 +21,4 @@ public KeySpliteratorShuffler(KeySpliterator ks) { public void forEachRemaining(Consumer action) { iter.forEachRemaining(action); } -} \ No newline at end of file +} 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 index 56570401..64b40d21 100644 --- a/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashKeySpliteratorASMDump.java +++ b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashKeySpliteratorASMDump.java @@ -131,25 +131,8 @@ public static byte[] dump () { methodVisitor.visitMaxs(2, 2); methodVisitor.visitEnd(); } - { - methodVisitor = classWriter.visitMethod(ACC_STATIC, "", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(7, label0); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn("YW=============shuffling ============"); - methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(8, label1); - methodVisitor.visitInsn(RETURN); - methodVisitor.visitMaxs(2, 0); - 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 index b5e82ce0..bbaef247 100644 --- a/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashMapKeySplitAdder.java +++ b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashMapKeySplitAdder.java @@ -1,8 +1,42 @@ +/* +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.*; +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); } @@ -19,20 +53,51 @@ public void addSplitShufflerField() { } public void addForEachRemaining() { - MethodVisitor methodVisitor = super.visitMethod(Opcodes.ACC_PUBLIC, "forEachRemaining", "(Ljava/util/function/Consumer;)V", "(Ljava/util/function/Consumer<-TK;>;)V", null); + 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.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); + 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.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(); } @@ -41,26 +106,44 @@ public void addForEachRemaining() { public void visitEnd() { addSplitShufflerField(); addForEachRemaining(); - super.visitInnerClass("java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", "java/util/HashMap$KeySpliterator", - "KeySpliteratorShuffler", 0); + 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) { + 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); // Push "this" (KeySpliterator) - super.visitTypeInsn(Opcodes.NEW, "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler"); // Create new KeySpliteratorShuffler - super.visitInsn(Opcodes.DUP); // Duplicate the reference to the new object - super.visitVarInsn(Opcodes.ALOAD, 0); // Push "this" again for the constructor - super.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler", - "", "(Ljava/util/HashMap$KeySpliterator;)V", false); // Call constructor - super.visitFieldInsn(Opcodes.PUTFIELD, "java/util/HashMap$KeySpliterator", "shuffler", - "Ljava/util/HashMap$KeySpliterator$KeySpliteratorShuffler;"); // Assign to shuffler + 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); } 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 5181fa40..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 @@ -73,7 +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 hashMapKeySpliteratorShufflerName = "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler.class"; + public static final String hashMapKeySpliShufflerName = "java/util/HashMap$KeySpliterator$KeySpliteratorShuffler.class"; private static final String rootPath = "modules/java.base"; @@ -206,9 +206,8 @@ public byte[] apply() { } }); } - // add SpliteratorShuffler.class - this.addAsmDumpResultToZip(outZip, hashMapKeySpliteratorShufflerName, new Producer() { + this.addAsmDumpResultToZip(outZip, hashMapKeySpliShufflerName, new Producer() { @Override public byte[] apply() { return HashKeySpliteratorASMDump.dump(); From d5c39d69eeb61b5b4ef2953aa7411f036ec3528a Mon Sep 17 00:00:00 2001 From: Yusen Wang Date: Sat, 30 Nov 2024 13:03:22 -0600 Subject: [PATCH 3/3] fix tryAdvance: sequential stream use both tryAdvance & forEachRemaning) --- .../src/main/java/java/util/HashMap.java | 4 ++ .../java/util/KeySpliteratorShuffler.java | 10 ++++ .../instr/HashKeySpliteratorASMDump.java | 40 +++++++++++++- .../nondex/instr/HashMapKeySplitAdder.java | 55 +++++++++++++++++++ 4 files changed, 108 insertions(+), 1 deletion(-) diff --git a/nondex-core/src/main/java/java/util/HashMap.java b/nondex-core/src/main/java/java/util/HashMap.java index 3d3e6dc2..5f8efc82 100644 --- a/nondex-core/src/main/java/java/util/HashMap.java +++ b/nondex-core/src/main/java/java/util/HashMap.java @@ -1567,6 +1567,10 @@ public void original_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 index fadb1df1..5ce548b2 100644 --- a/nondex-core/src/main/java/java/util/KeySpliteratorShuffler.java +++ b/nondex-core/src/main/java/java/util/KeySpliteratorShuffler.java @@ -21,4 +21,14 @@ public KeySpliteratorShuffler(KeySpliterator ks) { 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/HashKeySpliteratorASMDump.java b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashKeySpliteratorASMDump.java index 64b40d21..52583bca 100644 --- a/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashKeySpliteratorASMDump.java +++ b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashKeySpliteratorASMDump.java @@ -13,9 +13,10 @@ import org.objectweb.asm.RecordComponentVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.TypePath; + public class HashKeySpliteratorASMDump implements Opcodes { - public static byte[] dump () { + public static byte[] dump() { ClassWriter classWriter = new ClassWriter(0); FieldVisitor fieldVisitor; @@ -113,6 +114,43 @@ public static byte[] dump () { 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(); 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 index bbaef247..770a00c9 100644 --- a/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashMapKeySplitAdder.java +++ b/nondex-instrumentation/src/main/java/edu/illinois/nondex/instr/HashMapKeySplitAdder.java @@ -102,10 +102,62 @@ public void addForEachRemaining() { 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", @@ -153,6 +205,9 @@ public void visitInsn(int opcode) { // 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); } }