This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-bcel.git
The following commit(s) were added to refs/heads/master by this push:
new 604b7bdb Fix ConstantPoolGen dedup key collisions for names with
delimiters (#510)
604b7bdb is described below
commit 604b7bdb2494579b071d6645f86878c0e618a759
Author: Dexter.k <[email protected]>
AuthorDate: Thu Jul 2 21:07:32 2026 +0000
Fix ConstantPoolGen dedup key collisions for names with delimiters (#510)
Class and member names may legally contain the ':', '#', '&' and '%'
characters used to delimit the constant pool dedup-table keys, so two distinct
references whose names contain one collapsed to a single constant pool entry.
Length-prefix each key part so distinct triples stay distinct.
---
.../org/apache/bcel/generic/ConstantPoolGen.java | 57 ++++++++++---------
.../generic/ConstantPoolGenKeyCollisionTest.java | 64 ++++++++++++++++++++++
2 files changed, 92 insertions(+), 29 deletions(-)
diff --git a/src/main/java/org/apache/bcel/generic/ConstantPoolGen.java
b/src/main/java/org/apache/bcel/generic/ConstantPoolGen.java
index 019a6ea9..4c3a1740 100644
--- a/src/main/java/org/apache/bcel/generic/ConstantPoolGen.java
+++ b/src/main/java/org/apache/bcel/generic/ConstantPoolGen.java
@@ -59,8 +59,6 @@ public class ConstantPoolGen {
private static final String FIELDREF_DELIM = "&";
- private static final String NAT_DELIM = "%"; // Name and Type
-
/**
* @deprecated (since 6.0) will be made private; do not access directly,
use getter/setter
*/
@@ -103,8 +101,6 @@ public class ConstantPoolGen {
* @param cs array of given constants, new ones will be appended.
*/
public ConstantPoolGen(final Constant[] cs) {
- final StringBuilder sb = new StringBuilder(DEFAULT_BUFFER_SIZE);
-
size = Math.min(Math.max(DEFAULT_BUFFER_SIZE, cs.length + 64),
Const.MAX_CP_ENTRIES + 1);
constants = Arrays.copyOf(cs, size);
@@ -132,13 +128,7 @@ public class ConstantPoolGen {
final ConstantNameAndType n = (ConstantNameAndType) c;
final ConstantUtf8 u8NameIdx = (ConstantUtf8)
constants[n.getNameIndex()];
final ConstantUtf8 u8SigIdx = (ConstantUtf8)
constants[n.getSignatureIndex()];
-
- sb.append(u8NameIdx.getBytes());
- sb.append(NAT_DELIM);
- sb.append(u8SigIdx.getBytes());
- final String key = sb.toString();
- sb.delete(0, sb.length());
-
+ final String key = toKey(u8NameIdx.getBytes(),
u8SigIdx.getBytes());
if (!natTable.containsKey(key)) {
natTable.put(key, Integer.valueOf(i));
}
@@ -169,22 +159,14 @@ public class ConstantPoolGen {
u8 = (ConstantUtf8) constants[n.getSignatureIndex()];
final String signature = u8.getBytes();
- // Since name cannot begin with digit, we can use
METHODREF_DELIM without fear of duplicates
+ // Distinguishes the three kinds of reference that share
cpTable.
String delim = METHODREF_DELIM;
if (c instanceof ConstantInterfaceMethodref) {
delim = IMETHODREF_DELIM;
} else if (c instanceof ConstantFieldref) {
delim = FIELDREF_DELIM;
}
-
- sb.append(className);
- sb.append(delim);
- sb.append(methodName);
- sb.append(delim);
- sb.append(signature);
- final String key = sb.toString();
- sb.delete(0, sb.length());
-
+ final String key = toKey(delim, className, methodName,
signature);
if (!cpTable.containsKey(key)) {
cpTable.put(key, Integer.valueOf(i));
}
@@ -365,7 +347,7 @@ public class ConstantPoolGen {
final int nameAndTypeIndex = addNameAndType(fieldName, signature);
final int ret = index;
constants[index++] = new ConstantFieldref(classIndex,
nameAndTypeIndex);
- return computeIfAbsent(cpTable, className + FIELDREF_DELIM + fieldName
+ FIELDREF_DELIM + signature, ret);
+ return computeIfAbsent(cpTable, toKey(FIELDREF_DELIM, className,
fieldName, signature), ret);
}
/**
@@ -430,7 +412,7 @@ public class ConstantPoolGen {
final int nameAndTypeIndex = addNameAndType(methodName, signature);
final int ret = index;
constants[index++] = new ConstantInterfaceMethodref(classIndex,
nameAndTypeIndex);
- return computeIfAbsent(cpTable, className + IMETHODREF_DELIM +
methodName + IMETHODREF_DELIM + signature, ret);
+ return computeIfAbsent(cpTable, toKey(IMETHODREF_DELIM, className,
methodName, signature), ret);
}
/**
@@ -479,7 +461,7 @@ public class ConstantPoolGen {
final int classIndex = addClass(className);
final int ret = index;
constants[index++] = new ConstantMethodref(classIndex,
nameAndTypeIndex);
- return computeIfAbsent(cpTable, className + METHODREF_DELIM +
methodName + METHODREF_DELIM + signature, ret);
+ return computeIfAbsent(cpTable, toKey(METHODREF_DELIM, className,
methodName, signature), ret);
}
/**
@@ -499,7 +481,7 @@ public class ConstantPoolGen {
final int signatureIndex = addUtf8(signature);
ret = index;
constants[index++] = new ConstantNameAndType(nameIndex,
signatureIndex);
- return computeIfAbsent(natTable, name + NAT_DELIM + signature, ret);
+ return computeIfAbsent(natTable, toKey(name, signature), ret);
}
/**
@@ -563,6 +545,23 @@ public class ConstantPoolGen {
return map.computeIfAbsent(key, k -> Integer.valueOf(value));
}
+ /**
+ * Builds a lookup key that stays collision-free even when the parts
contain the ASCII characters used as
+ * delimiters above. Class, member and signature names read from a class
file may legally contain those characters
+ * (the JVMS only forbids {@code . ; [ /} and, for members, {@code < >}),
so each part is prefixed with its length
+ * to keep distinct triples distinct.
+ *
+ * @param parts the key parts.
+ * @return a collision-free key.
+ */
+ private static String toKey(final String... parts) {
+ final StringBuilder buf = new StringBuilder();
+ for (final String part : parts) {
+ buf.append(part.length()).append(':').append(part);
+ }
+ return buf.toString();
+ }
+
/**
* Gets a constant pool entry at the specified index.
*
@@ -642,7 +641,7 @@ public class ConstantPoolGen {
* @return index on success, -1 otherwise.
*/
public int lookupFieldref(final String className, final String fieldName,
final String signature) {
- return getIndex(cpTable, className + FIELDREF_DELIM + fieldName +
FIELDREF_DELIM + signature);
+ return getIndex(cpTable, toKey(FIELDREF_DELIM, className, fieldName,
signature));
}
/**
@@ -701,7 +700,7 @@ public class ConstantPoolGen {
* @return index on success, -1 otherwise.
*/
public int lookupInterfaceMethodref(final String className, final String
methodName, final String signature) {
- return getIndex(cpTable, className + IMETHODREF_DELIM + methodName +
IMETHODREF_DELIM + signature);
+ return getIndex(cpTable, toKey(IMETHODREF_DELIM, className,
methodName, signature));
}
/**
@@ -741,7 +740,7 @@ public class ConstantPoolGen {
* @return index on success, -1 otherwise.
*/
public int lookupMethodref(final String className, final String
methodName, final String signature) {
- return getIndex(cpTable, className + METHODREF_DELIM + methodName +
METHODREF_DELIM + signature);
+ return getIndex(cpTable, toKey(METHODREF_DELIM, className, methodName,
signature));
}
/**
@@ -752,7 +751,7 @@ public class ConstantPoolGen {
* @return index on success, -1 otherwise.
*/
public int lookupNameAndType(final String name, final String signature) {
- return getIndex(natTable, name + NAT_DELIM + signature);
+ return getIndex(natTable, toKey(name, signature));
}
/**
diff --git
a/src/test/java/org/apache/bcel/generic/ConstantPoolGenKeyCollisionTest.java
b/src/test/java/org/apache/bcel/generic/ConstantPoolGenKeyCollisionTest.java
new file mode 100644
index 00000000..5e7db4ba
--- /dev/null
+++ b/src/test/java/org/apache/bcel/generic/ConstantPoolGenKeyCollisionTest.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bcel.generic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests that the constant pool dedup tables do not conflate distinct
references whose names contain one of the ASCII
+ * delimiter characters. The JVMS only forbids {@code . ; [ /} (and {@code <
>} for members) in names, so a class or
+ * member name may legally contain {@code : # &}.
+ */
+class ConstantPoolGenKeyCollisionTest {
+
+ @Test
+ void testFieldrefNamesContainingDelimiter() {
+ final ConstantPoolGen cpg = new ConstantPoolGen();
+ final int a = cpg.addFieldref("A", "b&c", "I");
+ final int b = cpg.addFieldref("A&b", "c", "I");
+ assertNotEquals(a, b, "distinct fieldrefs collided to constant pool
index " + a);
+ }
+
+ @Test
+ void testInterfaceMethodrefNamesContainingDelimiter() {
+ final ConstantPoolGen cpg = new ConstantPoolGen();
+ final int a = cpg.addInterfaceMethodref("I", "m#n", "()V");
+ final int b = cpg.addInterfaceMethodref("I#m", "n", "()V");
+ assertNotEquals(a, b, "distinct interface methodrefs collided to
constant pool index " + a);
+ }
+
+ @Test
+ void testMethodrefDedupStillWorks() {
+ final ConstantPoolGen cpg = new ConstantPoolGen();
+ final int a = cpg.addMethodref("Foo", "bar", "()V");
+ final int b = cpg.addMethodref("Foo", "bar", "()V");
+ assertEquals(a, b, "identical methodrefs must reuse the same constant
pool entry");
+ }
+
+ @Test
+ void testMethodrefNamesContainingDelimiter() {
+ final ConstantPoolGen cpg = new ConstantPoolGen();
+ final int a = cpg.addMethodref("Foo", "bar:baz", "()V");
+ final int b = cpg.addMethodref("Foo:bar", "baz", "()V");
+ assertNotEquals(a, b, "distinct methodrefs collided to constant pool
index " + a);
+ }
+}