Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,39 @@ public class ObjectReaderCreatorASM

protected final DynamicClassLoader classLoader;

/**
* Sanitizes a class simple name for use in generated ASM class names.
* Array types like "Class[]" become "ClassArray", and any other invalid
* characters are replaced with underscores.
*/
static String sanitizeClassName(String simpleName) {
if (simpleName == null || simpleName.isEmpty()) {
return "";
}
// Handle array types like "Class[]" -> "ClassArray"
if (simpleName.endsWith("[]")) {
String baseName = simpleName.substring(0, simpleName.length() - 2);
return sanitizeClassName(baseName) + "Array";
}
// Replace any characters that are invalid in class names
StringBuilder sb = null;
for (int i = 0; i < simpleName.length(); i++) {
char c = simpleName.charAt(i);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') {
if (sb != null) {
sb.append(c);
}
} else {
if (sb == null) {
sb = new StringBuilder(simpleName.length());
sb.append(simpleName, 0, i);
}
sb.append('_');
}
}
return sb != null ? sb.toString() : simpleName;
}

static final String METHOD_DESC_GET_ITEM_OBJECT_READER = "(" + DESC_JSON_READER + ")" + DESC_OBJECT_READER;
static final String METHOD_DESC_GET_OBJECT_READER_1 = "(" + DESC_JSON_READER + ")" + DESC_OBJECT_READER;
static final String METHOD_DESC_INIT = "(Ljava/lang/Class;" + DESC_SUPPLIER + DESC_FIELD_READER_ARRAY + ")V";
Expand Down Expand Up @@ -4319,7 +4352,7 @@ public ObjectReadContext(
this.fieldNameLengthMin = fieldNameLengthMin;
this.fieldNameLengthMax = fieldNameLengthMax;

String className = "ORG_" + seed.incrementAndGet() + "_" + fieldReaders.length + (objectClass == null ? "" : "_" + objectClass.getSimpleName());
String className = "ORG_" + seed.incrementAndGet() + "_" + fieldReaders.length + (objectClass == null ? "" : "_" + sanitizeClassName(objectClass.getSimpleName()));

Package pkg = ObjectReaderCreatorASM.class.getPackage();
if (pkg != null) {
Expand Down Expand Up @@ -4383,7 +4416,7 @@ private Function createValueConsumer0(
String className = (bytes ? "VBACG_" : "VCACG_")
+ seed.incrementAndGet()
+ "_" + fieldReaderArray.length
+ "_" + objectClass.getSimpleName();
+ "_" + sanitizeClassName(objectClass.getSimpleName());
String classNameType;
String classNameFull;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,39 @@ public class ObjectWriterCreatorASM
protected static final AtomicLong seed = new AtomicLong();
protected final DynamicClassLoader classLoader;

/**
* Sanitizes a class simple name for use in generated ASM class names.
* Array types like "Class[]" become "ClassArray", and any other invalid
* characters are replaced with underscores.
*/
static String sanitizeClassName(String simpleName) {
if (simpleName == null || simpleName.isEmpty()) {
return "";
}
// Handle array types like "Class[]" -> "ClassArray"
if (simpleName.endsWith("[]")) {
String baseName = simpleName.substring(0, simpleName.length() - 2);
return sanitizeClassName(baseName) + "Array";
}
// Replace any characters that are invalid in class names
StringBuilder sb = null;
for (int i = 0; i < simpleName.length(); i++) {
char c = simpleName.charAt(i);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') {
if (sb != null) {
sb.append(c);
}
} else {
if (sb == null) {
sb = new StringBuilder(simpleName.length());
sb.append(simpleName, 0, i);
}
sb.append('_');
}
}
return sb != null ? sb.toString() : simpleName;
}

static final String[] INTERFACES = {TYPE_OBJECT_WRITER};

static final String DESC_SYMBOL = desc(SymbolTable.class);
Expand Down Expand Up @@ -471,7 +504,7 @@ private ObjectWriterAdapter jitWriter(

ClassWriter cw = new ClassWriter(null);

String className = "OWG_" + seed.incrementAndGet() + "_" + fieldWriters.size() + (objectClass == null ? "" : ("_" + objectClass.getSimpleName()));
String className = "OWG_" + seed.incrementAndGet() + "_" + fieldWriters.size() + (objectClass == null ? "" : ("_" + sanitizeClassName(objectClass.getSimpleName())));
String classNameType;
String classNameFull;

Expand Down Expand Up @@ -4628,7 +4661,7 @@ private FieldWriter jitFieldWriterList(

ClassWriter cw = new ClassWriter(null);

String className = "OWF_" + seed.incrementAndGet() + "_" + fieldWriters.size() + "_" + itemClass.getSimpleName();
String className = "OWF_" + seed.incrementAndGet() + "_" + fieldWriters.size() + "_" + sanitizeClassName(itemClass.getSimpleName());
String classNameType;
String classNameFull;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.alibaba.fastjson2.issues_4000;

import com.alibaba.fastjson2.JSON;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class Issue4009Test {
@Test
public void testClassArrayField() {
// Test that a class with a Class[] field can be serialized without ClassFormatError
BeanWithClassArray bean = new BeanWithClassArray();
bean.setClasses(new Class[]{String.class, Integer.class});
bean.setName("test");

// This should not throw ClassFormatError
Object json = JSON.toJSON(bean);
assertNotNull(json);

String jsonString = JSON.toJSONString(bean);
assertNotNull(jsonString);
assertTrue(jsonString.contains("test"));
}

@Test
public void testClassArrayFieldNull() {
BeanWithClassArray bean = new BeanWithClassArray();
bean.setClasses(null);
bean.setName("test");

Object json = JSON.toJSON(bean);
assertNotNull(json);
}

@Test
public void test2DArrayField() {
// Test multi-dimensional arrays
BeanWith2DArray bean = new BeanWith2DArray();
bean.setMatrix(new int[][]{{1, 2}, {3, 4}});

Object json = JSON.toJSON(bean);
assertNotNull(json);

String jsonString = JSON.toJSONString(bean);
assertNotNull(jsonString);
}

public static class BeanWithClassArray {
private String name;
private Class<?>[] classes;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Class<?>[] getClasses() {
return classes;
}

public void setClasses(Class<?>[] classes) {
this.classes = classes;
}
}

public static class BeanWith2DArray {
private int[][] matrix;

public int[][] getMatrix() {
return matrix;
}

public void setMatrix(int[][] matrix) {
this.matrix = matrix;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.alibaba.fastjson2.reader;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class ObjectReaderCreatorASMTest {
@Test
public void sanitizeClassName_null() {
assertEquals("", ObjectReaderCreatorASM.sanitizeClassName(null));
}

@Test
public void sanitizeClassName_empty() {
assertEquals("", ObjectReaderCreatorASM.sanitizeClassName(""));
}

@Test
public void sanitizeClassName_normal() {
assertEquals("String", ObjectReaderCreatorASM.sanitizeClassName("String"));
assertEquals("MyClass", ObjectReaderCreatorASM.sanitizeClassName("MyClass"));
assertEquals("MyClass2", ObjectReaderCreatorASM.sanitizeClassName("MyClass2"));
}

@Test
public void sanitizeClassName_array() {
assertEquals("ClassArray", ObjectReaderCreatorASM.sanitizeClassName("Class[]"));
assertEquals("StringArray", ObjectReaderCreatorASM.sanitizeClassName("String[]"));
assertEquals("IntegerArray", ObjectReaderCreatorASM.sanitizeClassName("Integer[]"));
}

@Test
public void sanitizeClassName_2dArray() {
assertEquals("intArrayArray", ObjectReaderCreatorASM.sanitizeClassName("int[][]"));
}

@Test
public void sanitizeClassName_specialChars() {
assertEquals("My_Class", ObjectReaderCreatorASM.sanitizeClassName("My$Class"));
assertEquals("My_Class_2", ObjectReaderCreatorASM.sanitizeClassName("My-Class-2"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.alibaba.fastjson2.writer;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class ObjectWriterCreatorASMTest {
@Test
public void sanitizeClassName_null() {
assertEquals("", ObjectWriterCreatorASM.sanitizeClassName(null));
}

@Test
public void sanitizeClassName_empty() {
assertEquals("", ObjectWriterCreatorASM.sanitizeClassName(""));
}

@Test
public void sanitizeClassName_normal() {
assertEquals("String", ObjectWriterCreatorASM.sanitizeClassName("String"));
assertEquals("MyClass", ObjectWriterCreatorASM.sanitizeClassName("MyClass"));
assertEquals("MyClass2", ObjectWriterCreatorASM.sanitizeClassName("MyClass2"));
}

@Test
public void sanitizeClassName_array() {
assertEquals("ClassArray", ObjectWriterCreatorASM.sanitizeClassName("Class[]"));
assertEquals("StringArray", ObjectWriterCreatorASM.sanitizeClassName("String[]"));
assertEquals("IntegerArray", ObjectWriterCreatorASM.sanitizeClassName("Integer[]"));
}

@Test
public void sanitizeClassName_2dArray() {
assertEquals("intArrayArray", ObjectWriterCreatorASM.sanitizeClassName("int[][]"));
}

@Test
public void sanitizeClassName_specialChars() {
assertEquals("My_Class", ObjectWriterCreatorASM.sanitizeClassName("My$Class"));
assertEquals("My_Class_2", ObjectWriterCreatorASM.sanitizeClassName("My-Class-2"));
}
}
Loading