diff options
author | ExE Boss <ExE-Boss@code.ExE-Boss.tech> | 2022-09-06 09:30:00 +0000 |
---|---|---|
committer | ExE Boss <ExE-Boss@code.ExE-Boss.tech> | 2022-09-06 09:32:18 +0000 |
commit | b06844ff06740ccdbb7e24a898afa6530f1b66a1 (patch) | |
tree | 41b45fc1817fe5f2d431ad4617242e9976a0ae49 | |
parent | 965c0069c2ed9818c2ee91469ec3ef0e8534c36b (diff) | |
download | ow2-asm-b06844ff06740ccdbb7e24a898afa6530f1b66a1.tar.gz |
Fix `SignatureWriter` when a generic type has a depth over 30
-rw-r--r-- | asm/src/main/java/org/objectweb/asm/signature/SignatureWriter.java | 55 | ||||
-rw-r--r-- | asm/src/test/java/org/objectweb/asm/signature/SignatureWriterTest.java | 43 |
2 files changed, 90 insertions, 8 deletions
diff --git a/asm/src/main/java/org/objectweb/asm/signature/SignatureWriter.java b/asm/src/main/java/org/objectweb/asm/signature/SignatureWriter.java index 2217a88a..327521af 100644 --- a/asm/src/main/java/org/objectweb/asm/signature/SignatureWriter.java +++ b/asm/src/main/java/org/objectweb/asm/signature/SignatureWriter.java @@ -27,6 +27,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. package org.objectweb.asm.signature; +import java.util.ArrayList; +import java.util.List; import org.objectweb.asm.Opcodes; /** @@ -52,7 +54,7 @@ public class SignatureWriter extends SignatureVisitor { /** * The stack used to keep track of class types that have arguments. Each element of this stack is * a boolean encoded in one bit. The top of the stack is the least significant bit. Pushing false - * = *2, pushing true = *2+1, popping = /2. + * {@code <<= 1}, pushing true {@code <<= 1; |= 1}, popping {@code >>>= 1}. * * <p>Class type arguments must be surrounded with '<' and '>' and, because * @@ -62,12 +64,18 @@ public class SignatureWriter extends SignatureVisitor { * SignatureWriter instances), * </ol> * - * <p>we need a stack to properly balance these 'parentheses'. A new element is pushed on this + * <p>we need a stack to properly balance these angle brackets. A new element is pushed on this * stack for each new visited type, and popped when the visit of this type ends (either is * visitEnd, or because visitInnerClassType is called). */ private int argumentStack; + /** The sum of the depths of {@link #argumentStack} and {@link #deepArgStack}. */ + private long argumentStackDepth; + + /** Used when {@link #argumentStack} can no longer fit into a single 32-bit integer. */ + private List<Integer> deepArgStack; + /** Constructs a new {@link SignatureWriter}. */ public SignatureWriter() { super(/* latest api =*/ Opcodes.ASM9); @@ -159,7 +167,7 @@ public class SignatureWriter extends SignatureVisitor { stringBuilder.append(name); // Pushes 'false' on the stack, meaning that this type does not have type arguments (as far as // we can tell at this point). - argumentStack *= 2; + pushArgStack(false); } @Override @@ -169,7 +177,7 @@ public class SignatureWriter extends SignatureVisitor { stringBuilder.append(name); // Pushes 'false' on the stack, meaning that this type does not have type arguments (as far as // we can tell at this point). - argumentStack *= 2; + pushArgStack(false); } @Override @@ -177,7 +185,7 @@ public class SignatureWriter extends SignatureVisitor { // If the top of the stack is 'false', this means we are visiting the first type argument of the // currently visited type. We therefore need to append a '<', and to replace the top stack // element with 'true' (meaning that the current type does have type arguments). - if (argumentStack % 2 == 0) { + if (argumentStackDepth > 0 && !peekArgStack()) { argumentStack |= 1; stringBuilder.append('<'); } @@ -189,7 +197,7 @@ public class SignatureWriter extends SignatureVisitor { // If the top of the stack is 'false', this means we are visiting the first type argument of the // currently visited type. We therefore need to append a '<', and to replace the top stack // element with 'true' (meaning that the current type does have type arguments). - if (argumentStack % 2 == 0) { + if (argumentStackDepth > 0 && !peekArgStack()) { argumentStack |= 1; stringBuilder.append('<'); } @@ -232,9 +240,40 @@ public class SignatureWriter extends SignatureVisitor { // If the top of the stack is 'true', this means that some type arguments have been visited for // the type whose visit is now ending. We therefore need to append a '>', and to pop one element // from the stack. - if (argumentStack % 2 == 1) { + if (argumentStackDepth > 0 && popArgStack()) { stringBuilder.append('>'); } - argumentStack /= 2; + } + + private boolean peekArgStack() { + assert argumentStackDepth > 0 : this; + return (argumentStack & 1) == 1; + } + + private void pushArgStack(boolean state) { + if (argumentStackDepth > 0 && (argumentStackDepth % 32) == 0) { + if (deepArgStack == null) { + deepArgStack = new ArrayList<>(5); + } + deepArgStack.add(argumentStack); + argumentStack = 0; + } else { + argumentStack <<= 1; + } + argumentStackDepth++; + if (state) { + argumentStack |= 1; + } + } + + private boolean popArgStack() { + assert argumentStackDepth > 0 : this; + boolean result = (argumentStack & 1) == 1; + argumentStack >>>= 1; + argumentStackDepth--; + if (argumentStackDepth > 0 && (argumentStackDepth % 32) == 0) { + argumentStack = deepArgStack.remove(deepArgStack.size() - 1); + } + return result; } } diff --git a/asm/src/test/java/org/objectweb/asm/signature/SignatureWriterTest.java b/asm/src/test/java/org/objectweb/asm/signature/SignatureWriterTest.java index be1ba012..e9aefa62 100644 --- a/asm/src/test/java/org/objectweb/asm/signature/SignatureWriterTest.java +++ b/asm/src/test/java/org/objectweb/asm/signature/SignatureWriterTest.java @@ -29,6 +29,7 @@ package org.objectweb.asm.signature; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.stream.IntStream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.objectweb.asm.test.AsmTest; @@ -62,4 +63,46 @@ class SignatureWriterTest extends AsmTest { assertEquals(signature, signatureWriter.toString()); } + + static IntStream deepSignatures() { + return IntStream.range(0, 48); + } + + @ParameterizedTest + @MethodSource("deepSignatures") + void testWrite_deepSignature(int depth) { + SignatureWriter signatureWriter = new SignatureWriter(); + String expected = writeDeepSignature(signatureWriter, depth); + assertEquals(expected, signatureWriter.toString(), "depth=" + depth); + } + + private String writeDeepSignature(SignatureVisitor signatureVisitor, int maxDepth) { + StringBuilder expected = new StringBuilder(); + writeDeepSignatureInner(signatureVisitor, expected, 0, maxDepth); + return expected.toString(); + } + + private void writeDeepSignatureInner(SignatureVisitor signatureVisitor, StringBuilder expected, int currentDepth, int maxDepth) { + signatureVisitor.visitClassType(TEST_GENERIC); + expected.append('L' + TEST_GENERIC); + if (currentDepth < maxDepth) { + expected.append("<L" + TEST_OPEN + ';'); + SignatureVisitor v = signatureVisitor.visitTypeArgument('='); + v.visitClassType(TEST_OPEN); + v.visitEnd(); + + writeDeepSignatureInner(signatureVisitor.visitTypeArgument('='), expected, currentDepth + 1, maxDepth); + + v = signatureVisitor.visitTypeArgument('='); + v.visitClassType(TEST_CLOSE); + v.visitEnd(); + expected.append('L' + TEST_CLOSE + ";>"); + } + signatureVisitor.visitEnd(); + expected.append(';'); + } + + private static final String TEST_OPEN = "TestOpen"; + private static final String TEST_GENERIC = "TestGeneric"; + private static final String TEST_CLOSE = "TestClose"; } |