optimization for jsonb deserialize non default constructor

This commit is contained in:
wenshao 2025-03-17 08:50:42 +08:00 committed by Shaojin Wen
parent 58dc6f5576
commit b8aaa09631
7 changed files with 330 additions and 37 deletions

View File

@ -1,5 +1,6 @@
package com.alibaba.fastjson2.reader;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.schema.JSONSchema;
@ -35,7 +36,12 @@ final class FieldReaderInt16Param<T>
@Override
public Object readFieldValue(JSONReader jsonReader) {
Integer integer = jsonReader.readInt32();
return integer == null ? null : integer.shortValue();
Integer value = jsonReader.readInt32();
if (value == null
&& fieldClass == byte.class
&& (jsonReader.features(this.features) & JSONReader.Feature.ErrorOnNullForPrimitives.mask) != 0) {
throw new JSONException(jsonReader.info("short value not support input null"));
}
return value == null ? null : value.shortValue();
}
}

View File

@ -1,5 +1,6 @@
package com.alibaba.fastjson2.reader;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.schema.JSONSchema;
@ -24,6 +25,12 @@ final class FieldReaderInt32Param<T>
@Override
public Object readFieldValue(JSONReader jsonReader) {
return jsonReader.readInt32();
Integer value = jsonReader.readInt32();
if (value == null
&& fieldClass == int.class
&& (jsonReader.features(this.features) & JSONReader.Feature.ErrorOnNullForPrimitives.mask) != 0) {
throw new JSONException(jsonReader.info("int value not support input null"));
}
return value;
}
}

View File

@ -1,5 +1,6 @@
package com.alibaba.fastjson2.reader;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.schema.JSONSchema;
@ -25,6 +26,12 @@ final class FieldReaderInt64Param<T>
@Override
public Object readFieldValue(JSONReader jsonReader) {
return jsonReader.readInt64();
Long value = jsonReader.readInt64();
if (value == null
&& fieldClass == long.class
&& (jsonReader.features(this.features) & JSONReader.Feature.ErrorOnNullForPrimitives.mask) != 0) {
throw new JSONException(jsonReader.info("long value not support input null"));
}
return value;
}
}

View File

@ -201,6 +201,12 @@ public class FieldReaderObject<T>
}
Object value = initReader.readJSONBObject(jsonReader, fieldType, fieldName, features);
if (value == null
&& (jsonReader.features(this.features) & JSONReader.Feature.ErrorOnNullForPrimitives.mask) != 0
&& fieldClass.isPrimitive()
) {
throw new JSONException(jsonReader.info("primitive value not support input null"));
}
accept(object, value);
}

View File

@ -454,6 +454,10 @@ public class ObjectReaderCreatorASM
genMethodReadObject(context, beanInfo.readerFeatures);
if (!context.disableJSONB()) {
genMethodReadJSONBObject(context, beanInfo.readerFeatures);
}
byte[] code = cw.toByteArray();
try {
Class<?> readerClass = classLoader.defineClassPublic(context.classNameFull, code, 0, code.length);
@ -1028,14 +1032,14 @@ public class ObjectReaderCreatorASM
}
if (!disableArrayMapping) {
Label object_ = new Label();
Label L0 = new Label();
// if (jsonReader.isArray() && jsonReader.isSupportBeanArray()) {
{
Label startArray_ = new Label(), endArray_ = new Label();
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "isArray", "()Z");
mw.ifeq(object_);
mw.ifeq(L0);
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "isSupportBeanArray", "()Z");
@ -1052,40 +1056,99 @@ public class ObjectReaderCreatorASM
mw.visitLabel(endArray_);
}
mw.visitLabel(object_);
mw.visitLabel(L0);
}
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
{
/*
* if (jsonReader.hasAutoTypeBeforeHandler()
* || (features & (JSONReader.Feature.SupportSmartMatch.mask | JSONReader.Feature.SupportAutoType.mask)) != 0
* ) {
* return super.readObject(jsonReader, fieldType, fieldName, features);
* }
*/
Label L3 = new Label(), L4 = new Label();
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "hasAutoTypeBeforeHandler", "()Z");
mw.ifne(L3);
mw.lload(FEATURES);
mw.visitLdcInsn(JSONReader.Feature.SupportSmartMatch.mask | JSONReader.Feature.SupportAutoType.mask);
mw.land();
mw.lconst_0();
mw.lcmp();
mw.ifeq(L4);
mw.visitLabel(L3);
mw.aload(THIS);
mw.aload(JSON_READER);
mw.aload(FIELD_TYPE);
mw.aload(FIELD_NAME);
mw.lload(FEATURES);
mw.invokespecial(TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR, "readJSONBObject", METHOD_DESC_READ_OBJECT);
mw.areturn();
mw.visitLabel(L4);
}
for (FieldReader fieldReader : fieldReaderArray) {
Class fieldClass = fieldReader.fieldClass;
int var = mwc.var(fieldReader);
if (fieldClass == byte.class || fieldClass == short.class || fieldClass == int.class || fieldClass == boolean.class || fieldClass == char.class) {
mw.iconst_0();
mw.istore(var);
} else if (fieldClass == long.class) {
mw.lconst_0();
mw.lstore(var);
} else if (fieldClass == float.class) {
mw.iconst_0();
mw.i2f();
mw.fstore(var);
} else if (fieldClass == double.class) {
mw.iconst_0();
mw.i2d();
mw.dstore(var);
} else {
mw.aconst_null();
mw.astore(var);
}
}
} else {
genCreateObject(mw, context, classNameType);
mw.astore(OBJECT);
}
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "nextIfObjectStart", "()Z");
mw.pop();
// for (int i = 0; i < entry_cnt; ++i) {
Label for_start_i_ = new Label(), for_end_i_ = new Label(), for_inc_i_ = new Label();
Label L_FOR_START = new Label(), L_FOR_END = new Label(), L_FOR_INC = new Label();
if (!disableAutoType) {
mw.iconst_0();
mw.istore(I);
}
mw.visitLabel(for_start_i_);
mw.visitLabel(L_FOR_START);
Label hashCode64Start = new Label();
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "nextIfObjectEnd", "()Z");
mw.ifne(for_end_i_);
mw.ifne(L_FOR_END);
boolean switchGen = false;
if (context.fieldNameLengthMin >= 2 && context.fieldNameLengthMax <= 43) {
genRead243(
context,
fieldBased,
mwc,
OBJECT,
for_inc_i_,
L_FOR_INC,
hashCode64Start
);
switchGen = true;
}
mw.visitLabel(hashCode64Start);
@ -1096,9 +1159,14 @@ public class ObjectReaderCreatorASM
mw.lstore(HASH_CODE64);
mw.lconst_0();
mw.lcmp();
mw.ifeq(for_inc_i_);
mw.ifeq(L_FOR_INC);
if (!disableAutoType) {
if (!disableAutoType && !(context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor)) {
/*
* if (hashCode64 == this.typeKeyHashCode() && hashCode64 != 0) {
* object = this.autoType(jsonReader);
* }
*/
Label endAutoType_ = new Label();
mw.lload(HASH_CODE64);
mw.aload(THIS);
@ -1116,13 +1184,63 @@ public class ObjectReaderCreatorASM
mw.aload(JSON_READER);
mw.invokevirtual(classNameType, "autoType", "(" + DESC_JSON_READER + ")Ljava/lang/Object;");
mw.astore(OBJECT);
mw.goto_(for_end_i_);
mw.goto_(L_FOR_END);
mw.visitLabel(endAutoType_);
}
if (switchGen) {
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
int ORIDINAL = mwc.var("ordinal");
mw.aload(THIS);
mw.lload(HASH_CODE64);
mw.invokevirtual(TYPE_OBJECT_READER_ADAPTER, "getFieldOrdinal", "(J)I");
mw.istore(ORIDINAL);
Label dflt = new Label();
Label[] labels = new Label[fieldReaderArray.length];
int[] switchKeys = new int[fieldReaderArray.length];
for (int i = 0; i < fieldReaderArray.length; i++) {
labels[i] = new Label();
switchKeys[i] = i;
}
mw.iload(ORIDINAL);
mw.visitLookupSwitchInsn(dflt, switchKeys, labels);
for (int i = 0; i < fieldReaderArray.length; i++) {
mw.visitLabel(labels[i]);
FieldReader fieldReader = fieldReaderArray[i];
genReadFieldValue(
context,
fieldReader,
fieldBased,
mwc,
OBJECT,
i,
false
);
mw.goto_(L_FOR_INC);
}
// jsonReader.skipValue();
mw.visitLabel(dflt);
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "skipValue", "()V");
} else {
/*
* this.readFieldValue(hashCode64, jsonReader, features, object);
*/
mw.aload(THIS);
mw.lload(HASH_CODE64);
mw.aload(JSON_READER);
mw.lload(FEATURES);
mw.aload(OBJECT);
mw.invokevirtual(TYPE_OBJECT_READER_ADAPTER, "readFieldValue", READ_FIELD_READER_UL);
}
mw.goto_(L_FOR_INC); // continue
// continue
if (fieldReaderArray.length > 6) {
} else if (fieldReaderArray.length > 6) {
// use switch
Map<Integer, List<Long>> map = new TreeMap();
@ -1188,14 +1306,14 @@ public class ObjectReaderCreatorASM
index,
true // JSONB
);
mw.goto_(for_inc_i_);
mw.goto_(L_FOR_INC);
if (next != dflt) {
mw.visitLabel(next);
}
}
mw.goto_(for_inc_i_);
mw.goto_(L_FOR_INC);
}
// switch_default
@ -1222,7 +1340,7 @@ public class ObjectReaderCreatorASM
mw.aload(JSON_READER);
mw.aload(OBJECT);
mw.invokevirtual(TYPE_FIELD_READE, "readFieldValueJSONB", METHOD_DESC_READ_FIELD_VALUE);
mw.goto_(for_inc_i_); // continue
mw.goto_(L_FOR_INC); // continue
mw.visitLabel(fieldReaderNull_);
} else {
@ -1248,7 +1366,7 @@ public class ObjectReaderCreatorASM
false // arrayMapping
);
mw.goto_(for_inc_i_); // continue
mw.goto_(L_FOR_INC); // continue
mw.visitLabel(next_);
}
@ -1288,29 +1406,89 @@ public class ObjectReaderCreatorASM
false // arrayMapping
);
mw.goto_(for_inc_i_); // continue
mw.goto_(L_FOR_INC); // continue
mw.visitLabel(next_);
}
mw.visitLabel(processExtra_);
}
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
mw.aload(JSON_READER);
mw.invokevirtual(TYPE_JSON_READER, "skipValue", "()V");
} else {
mw.aload(THIS);
mw.aload(JSON_READER);
mw.aload(OBJECT);
mw.lload(FEATURES);
mw.invokevirtual(TYPE_OBJECT_READER_ADAPTER, "processExtra", METHOD_DESC_PROCESS_EXTRA);
mw.goto_(for_inc_i_); // continue
}
mw.goto_(L_FOR_INC); // continue
mw.visitLabel(for_inc_i_);
mw.visitLabel(L_FOR_INC);
if (!disableAutoType) {
mw.visitIincInsn(I, 1);
}
mw.goto_(for_start_i_);
mw.goto_(L_FOR_START);
mw.visitLabel(for_end_i_);
mw.visitLabel(L_FOR_END);
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
ObjectReaderNoneDefaultConstructor objectReaderNoneDefaultConstructor = (ObjectReaderNoneDefaultConstructor) context.objectReaderAdapter;
boolean constructDirect = true;
if (classLoader.isExternalClass(context.objectClass)
|| context.objectClass.getTypeParameters().length != 0
|| (objectReaderNoneDefaultConstructor.constructor != null && !Modifier.isPublic(objectReaderNoneDefaultConstructor.constructor.getModifiers()))
|| (context.objectClass != null && !Modifier.isPublic(context.objectClass.getModifiers()))
|| objectReaderNoneDefaultConstructor.factoryFunction != null
|| objectReaderNoneDefaultConstructor.noneDefaultConstructor != null && !Modifier.isPublic(objectReaderNoneDefaultConstructor.noneDefaultConstructor.getModifiers())
) {
constructDirect = false;
}
if (constructDirect) {
mw.new_(context.objectType);
mw.dup();
StringBuilder buf = new StringBuilder().append("(");
for (FieldReader fieldReader : fieldReaderArray) {
mw.loadLocal(fieldReader.fieldClass, mwc.var(fieldReader));
buf.append(ASMUtils.desc(fieldReader.fieldClass));
}
buf.append(")V");
mw.invokespecial(context.objectType, "<init>", buf.toString());
} else {
mw.aload(THIS);
mw.iconst_n(fieldReaderArray.length);
mw.anewArray("java/lang/Object");
for (int i = 0; i < fieldReaderArray.length; i++) {
FieldReader fieldReader = fieldReaderArray[i];
mw.dup();
mw.iconst_n(i);
mw.loadLocal(fieldReader.fieldClass, mwc.var(fieldReader));
if (fieldReader.fieldClass == int.class) {
mw.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
} else if (fieldReader.fieldClass == long.class) {
mw.invokestatic("java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
} else if (fieldReader.fieldClass == float.class) {
mw.invokestatic("java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
} else if (fieldReader.fieldClass == double.class) {
mw.invokestatic("java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
} else if (fieldReader.fieldClass == boolean.class) {
mw.invokestatic("java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
} else if (fieldReader.fieldClass == short.class) {
mw.invokestatic("java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
} else if (fieldReader.fieldClass == byte.class) {
mw.invokestatic("java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
} else if (fieldReader.fieldClass == char.class) {
mw.invokestatic("java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
}
mw.aastore();
}
mw.invokevirtual(TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR, "createInstance", "([Ljava/lang/Object;)Ljava/lang/Object;");
}
} else {
mw.aload(OBJECT);
}
mw.areturn();
mw.visitMaxs(5, 10);

View File

@ -1,15 +1,13 @@
package com.alibaba.fastjson2.codec;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONB;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.*;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.Currency;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class NonDefaulConstructorTestTest2 {
@Test
@ -97,4 +95,76 @@ public class NonDefaulConstructorTestTest2 {
this.name = name;
}
}
@Test
public void test_bean1() {
JSONObject jsonObject = JSONObject.of("v0", 3);
{
String str = jsonObject.toString();
Bean1 a = JSON.parseObject(str, Bean1.class);
assertEquals(3, a.v0);
}
{
byte[] jsonb = JSONB.toBytes(jsonObject);
Bean1 a = JSONB.parseObject(jsonb, Bean1.class);
assertEquals(3, a.v0);
}
}
@Test
public void test_bean1_eror() {
byte[] jsonb = JSONObject.of("v0", null).toJSONBBytes(JSONWriter.Feature.WriteNulls);
assertThrows(
JSONException.class,
() -> JSONB.parseObject(jsonb, Bean1.class, JSONReader.Feature.ErrorOnNullForPrimitives));
}
public static class Bean1 {
final byte v0;
public Bean1(byte v0) {
this.v0 = v0;
}
}
@Test
public void test_ben6() {
JSONObject jsonObject = JSONObject.of("v0", 3);
String str = jsonObject.toString();
Bean6 a = JSON.parseObject(str, Bean6.class);
assertEquals(3, a.v0);
byte[] jsonb = JSONB.toBytes(jsonObject);
a = JSONB.parseObject(jsonb, Bean6.class);
assertEquals(3, a.v0);
}
@Test
public void test_ben6_error() {
byte[] jsonb = JSONObject.of("v0", null).toJSONBBytes(JSONWriter.Feature.WriteNulls);
assertThrows(
JSONException.class,
() -> JSONB.parseObject(jsonb, Bean6.class, JSONReader.Feature.ErrorOnNullForPrimitives));
}
public static class Bean6 {
final int v0;
final int v1;
final int v2;
final int v3;
final int v4;
final int v5;
final int v6;
public Bean6(int v0, int v1, int v2, int v3, int v4, int v5, int v6) {
this.v0 = v0;
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
this.v4 = v4;
this.v5 = v5;
this.v6 = v6;
}
}
}

View File

@ -21,6 +21,10 @@ public class RecordTest {
Item item2 = JSON.parseObject(str, Item.class);
assertEquals(item.value, item2.value);
byte[] jsonb = JSONB.toBytes(item, JSONWriter.Feature.UseSingleQuotes);
Item item3 = JSONB.parseObject(jsonb, Item.class);
assertEquals(item.value, item2.value);
}
public record Item(int value) {
@ -39,6 +43,11 @@ public class RecordTest {
Item1 item2 = JSON.parseObject(str, Item1.class);
assertEquals(item.value, item2.value);
}
{
byte[] jsonb = JSONB.toBytes(item, JSONWriter.Feature.UseSingleQuotes);
Item1 item2 = JSONB.parseObject(jsonb, Item1.class);
assertEquals(item.value, item2.value);
}
}
public record Item1(List<String> value) {
@ -51,6 +60,11 @@ public class RecordTest {
Item2 item2 = JSON.parseObject(str, Item2.class);
assertEquals(item.value.size(), item2.value.size());
assertEquals(item.value.get(0).value, item2.value.get(0).value);
byte[] jsonb = JSONB.toBytes(item, JSONWriter.Feature.UseSingleQuotes);
Item2 item3 = JSONB.parseObject(jsonb, Item2.class);
assertEquals(item.value.size(), item3.value.size());
assertEquals(item.value.get(0).value, item3.value.get(0).value);
}
public record Item2(List<Item> value) {
@ -95,6 +109,11 @@ public class RecordTest {
Item5 item2 = JSON.parseObject(str, Item5.class);
assertEquals(item.user.default_profile, item2.user.default_profile);
assertEquals(item.user.screen_name, item2.user.screen_name);
byte[] jsonb = JSONB.toBytes(item);
Item5 item3 = JSONB.parseObject(jsonb, Item5.class);
assertEquals(item.user.default_profile, item3.user.default_profile);
assertEquals(item.user.screen_name, item3.user.screen_name);
}
private record User5(boolean default_profile, String screen_name) {