summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorExE Boss <ExE-Boss@code.ExE-Boss.tech>2022-09-06 09:30:00 +0000
committerExE Boss <ExE-Boss@code.ExE-Boss.tech>2022-09-06 09:32:18 +0000
commitb06844ff06740ccdbb7e24a898afa6530f1b66a1 (patch)
tree41b45fc1817fe5f2d431ad4617242e9976a0ae49
parent965c0069c2ed9818c2ee91469ec3ef0e8534c36b (diff)
downloadow2-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.java55
-rw-r--r--asm/src/test/java/org/objectweb/asm/signature/SignatureWriterTest.java43
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 '&lt;' and '&gt;' 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";
}