blob: 48db2f889508f44ed55f6d89f32483d52d78e2f0 [file] [log] [blame]
/*
* 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
*
* http://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.fury.builder;
import static org.apache.fury.codegen.Expression.Invoke.inlineInvoke;
import static org.apache.fury.codegen.Expression.Reference.fieldRef;
import static org.apache.fury.codegen.ExpressionUtils.cast;
import static org.apache.fury.codegen.ExpressionUtils.eq;
import static org.apache.fury.codegen.ExpressionUtils.lessThan;
import static org.apache.fury.type.TypeUtils.OBJECT_ARRAY_TYPE;
import static org.apache.fury.type.TypeUtils.OBJECT_TYPE;
import static org.apache.fury.type.TypeUtils.PRIMITIVE_BOOLEAN_TYPE;
import static org.apache.fury.type.TypeUtils.PRIMITIVE_BYTE_TYPE;
import static org.apache.fury.type.TypeUtils.PRIMITIVE_INT_TYPE;
import static org.apache.fury.type.TypeUtils.PRIMITIVE_LONG_TYPE;
import static org.apache.fury.type.TypeUtils.PRIMITIVE_VOID_TYPE;
import static org.apache.fury.type.TypeUtils.getRawType;
import com.google.common.reflect.TypeToken;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.fury.Fury;
import org.apache.fury.builder.Generated.GeneratedCompatibleSerializer;
import org.apache.fury.codegen.CodegenContext;
import org.apache.fury.codegen.Expression;
import org.apache.fury.codegen.Expression.Assign;
import org.apache.fury.codegen.Expression.AssignArrayElem;
import org.apache.fury.codegen.Expression.BitAnd;
import org.apache.fury.codegen.Expression.BitOr;
import org.apache.fury.codegen.Expression.BitShift;
import org.apache.fury.codegen.Expression.Cast;
import org.apache.fury.codegen.Expression.Comparator;
import org.apache.fury.codegen.Expression.If;
import org.apache.fury.codegen.Expression.Invoke;
import org.apache.fury.codegen.Expression.ListExpression;
import org.apache.fury.codegen.Expression.Literal;
import org.apache.fury.codegen.Expression.Reference;
import org.apache.fury.codegen.Expression.Return;
import org.apache.fury.codegen.Expression.StaticInvoke;
import org.apache.fury.codegen.Expression.While;
import org.apache.fury.codegen.ExpressionOptimizer;
import org.apache.fury.codegen.ExpressionUtils;
import org.apache.fury.collection.Tuple2;
import org.apache.fury.resolver.ClassInfo;
import org.apache.fury.resolver.ClassResolver;
import org.apache.fury.resolver.FieldResolver;
import org.apache.fury.resolver.FieldResolver.CollectionFieldInfo;
import org.apache.fury.resolver.FieldResolver.FieldInfo;
import org.apache.fury.resolver.FieldResolver.FieldTypes;
import org.apache.fury.resolver.FieldResolver.MapFieldInfo;
import org.apache.fury.serializer.CompatibleSerializer;
import org.apache.fury.type.Descriptor;
import org.apache.fury.type.TypeUtils;
import org.apache.fury.util.Platform;
import org.apache.fury.util.Preconditions;
import org.apache.fury.util.ReflectionUtils;
import org.apache.fury.util.function.SerializableSupplier;
import org.apache.fury.util.record.RecordUtils;
/** A jit-version of {@link CompatibleSerializer}. */
public class CompatibleCodecBuilder extends BaseObjectCodecBuilder {
public static final String FIELD_RESOLVER_NAME = "fieldResolver";
private final FieldResolver fieldResolver;
private Map<String, Integer> recordReversedMapping;
private final Reference fieldResolverRef;
private final Literal endTagLiteral;
private final Map<String, Expression> methodCache;
public CompatibleCodecBuilder(
TypeToken<?> beanType,
Fury fury,
FieldResolver fieldResolver,
Class<?> superSerializerClass) {
// if not resolveParent, there won't be necessary to implement
// `CompatibleSerializerBase.readAndSetFields`.
super(beanType, fury, superSerializerClass);
this.fieldResolver = fieldResolver;
if (isRecord) {
List<String> fieldNames =
fieldResolver.getAllFieldsList().stream()
.map(FieldResolver.FieldInfo::getName)
.collect(Collectors.toList());
recordReversedMapping = RecordUtils.buildFieldToComponentMapping(beanClass);
}
ctx.reserveName(FIELD_RESOLVER_NAME);
endTagLiteral = new Literal(fieldResolver.getEndTag(), PRIMITIVE_LONG_TYPE);
TypeToken<FieldResolver> fieldResolverTypeToken = TypeToken.of(FieldResolver.class);
fieldResolverRef = fieldRef(FIELD_RESOLVER_NAME, fieldResolverTypeToken);
Expression fieldResolverExpr =
inlineInvoke(
classResolverRef,
"getFieldResolver",
fieldResolverTypeToken,
getClassExpr(getRawType(beanType)));
ctx.addField(ctx.type(fieldResolverTypeToken), FIELD_RESOLVER_NAME, fieldResolverExpr);
if (isRecord) {
buildRecordComponentDefaultValues();
}
methodCache = new HashMap<>();
}
@Override
protected String codecSuffix() {
return "Compatible";
}
@Override
protected void addCommonImports() {
super.addCommonImports();
ctx.addImport(GeneratedCompatibleSerializer.class);
}
@Override
protected boolean isMonomorphic(Class<?> clz) {
return ReflectionUtils.isMonomorphic(clz);
}
private Descriptor createDescriptor(FieldInfo fieldInfo) {
TypeToken<?> typeToken;
Field field = fieldInfo.getField();
if (fieldInfo instanceof MapFieldInfo) {
MapFieldInfo mapFieldInfo = (MapFieldInfo) fieldInfo;
// Remove nested generics such as `Map<Integer, Map<Integer, Collection<Integer>>>` to keep
// consistent with
// CompatibleSerializer.
// TODO support nested collection/map generics.
typeToken =
TypeUtils.mapOf(
mapFieldInfo.getType(), mapFieldInfo.getKeyType(), mapFieldInfo.getValueType());
} else {
typeToken = TypeToken.of(field.getGenericType());
}
Method readerMethod = null;
if (isRecord) {
try {
readerMethod = field.getDeclaringClass().getDeclaredMethod(field.getName());
} catch (NoSuchMethodException e) {
// impossible
Platform.throwException(e);
}
}
return new Descriptor(field, typeToken, readerMethod, null);
}
private Expression invokeGenerated(
CodegenContext ctx,
SerializableSupplier<Expression> expressionsGenerator,
String methodPrefix,
long fieldId) {
return methodCache.computeIfAbsent(
methodPrefix + fieldId,
id -> ExpressionOptimizer.invokeGenerated(ctx, expressionsGenerator, methodPrefix));
}
@Override
public Expression buildEncodeExpression() {
Reference inputObject = new Reference(ROOT_OBJECT_NAME, OBJECT_TYPE, false);
Reference buffer = new Reference(BUFFER_NAME, bufferTypeToken, false);
ListExpression expressions = new ListExpression();
Expression bean = tryCastIfPublic(inputObject, beanType, ctx.newName(beanClass));
expressions.add(bean);
groupFields(fieldResolver.getEmbedTypes4Fields(), 9)
.forEach(
group -> {
Expression invokeGeneratedWrite =
ExpressionOptimizer.invokeGenerated(
ctx,
() -> {
ListExpression groupExpressions = new ListExpression();
for (FieldInfo fieldInfo : group) {
groupExpressions.add(
new Invoke(
buffer,
"writeInt32",
new Literal(
(int) fieldInfo.getEncodedFieldInfo(), PRIMITIVE_INT_TYPE)));
groupExpressions.add(writeEmbedTypeFieldValue(bean, buffer, fieldInfo));
}
return groupExpressions;
},
"writeEmbedTypes4Fields");
expressions.add(invokeGeneratedWrite);
});
groupFields(fieldResolver.getEmbedTypes9Fields(), 9)
.forEach(
group -> {
Expression invokeGeneratedWrite =
ExpressionOptimizer.invokeGenerated(
ctx,
() -> {
ListExpression groupExpressions = new ListExpression();
for (FieldInfo fieldInfo : group) {
groupExpressions.add(
new Invoke(
buffer,
"writeInt64",
new Literal(
fieldInfo.getEncodedFieldInfo(), PRIMITIVE_LONG_TYPE)));
groupExpressions.add(writeEmbedTypeFieldValue(bean, buffer, fieldInfo));
}
return groupExpressions;
},
"writeEmbedTypes9Fields");
expressions.add(invokeGeneratedWrite);
});
groupFields(fieldResolver.getEmbedTypesHashFields(), 9)
.forEach(
group -> {
Expression invokeGeneratedWrite =
ExpressionOptimizer.invokeGenerated(
ctx,
() -> {
ListExpression groupExpressions = new ListExpression();
for (FieldInfo fieldInfo : group) {
groupExpressions.add(
new Invoke(
buffer,
"writeInt64",
new Literal(
fieldInfo.getEncodedFieldInfo(), PRIMITIVE_LONG_TYPE)));
groupExpressions.add(writeEmbedTypeFieldValue(bean, buffer, fieldInfo));
}
return groupExpressions;
},
"writeEmbedTypesHashFields");
expressions.add(invokeGeneratedWrite);
});
groupFields(fieldResolver.getSeparateTypesHashFields(), 9)
.forEach(
group -> {
Expression invokeGeneratedWrite =
ExpressionOptimizer.invokeGenerated(
ctx,
() -> {
ListExpression groupExpressions = new ListExpression();
for (FieldInfo fieldInfo : group) {
groupExpressions.add(
new Invoke(
buffer,
"writeInt64",
new Literal(
fieldInfo.getEncodedFieldInfo(), PRIMITIVE_LONG_TYPE)));
Descriptor descriptor = createDescriptor(fieldInfo);
walkPath.add(descriptor.getDeclaringClass() + descriptor.getName());
byte fieldType = fieldInfo.getFieldType();
Expression writeFieldAction =
invokeGenerated(
ctx,
() -> {
Expression fieldValue = getFieldValue(bean, descriptor);
ListExpression writeFieldValue =
new ListExpression(
new Invoke(
buffer,
"writeByte",
new Literal(fieldType, PRIMITIVE_BYTE_TYPE)));
if (fieldType == FieldTypes.OBJECT) {
writeFieldValue.add(
writeForNotNullNonFinalObject(
fieldValue, buffer, descriptor.getTypeToken()));
} else {
if (fieldType == FieldTypes.COLLECTION_ELEMENT_FINAL) {
CollectionFieldInfo collectionFieldInfo =
(CollectionFieldInfo) fieldInfo;
writeFieldValue.add(
writeFinalClassInfo(
buffer, collectionFieldInfo.getElementType()));
} else if (fieldType == FieldTypes.MAP_KV_FINAL) {
MapFieldInfo mapFieldInfo = (MapFieldInfo) fieldInfo;
Expression keyClassInfo =
getFinalClassInfo(mapFieldInfo.getKeyType());
Expression valueClassInfo =
getFinalClassInfo(mapFieldInfo.getValueType());
writeFieldValue.add(
keyClassInfo,
valueClassInfo,
writeFinalClassInfo(buffer, mapFieldInfo.getKeyType()),
writeFinalClassInfo(
buffer, mapFieldInfo.getValueType()));
} else if (fieldType == FieldTypes.MAP_KEY_FINAL) {
MapFieldInfo mapFieldInfo = (MapFieldInfo) fieldInfo;
writeFieldValue.add(
writeFinalClassInfo(buffer, mapFieldInfo.getKeyType()));
} else {
Preconditions.checkArgument(
fieldType == FieldTypes.MAP_VALUE_FINAL, fieldInfo);
MapFieldInfo mapFieldInfo = (MapFieldInfo) fieldInfo;
writeFieldValue.add(
writeFinalClassInfo(
buffer, mapFieldInfo.getValueType()));
}
Class<?> clz = descriptor.getRawType();
if (ReflectionUtils.isMonomorphic(clz)) {
// serializeForNotNull won't write field type if it's final,
// but the type is useful if peer doesn't have this field.
writeFieldValue.add(writeFinalClassInfo(buffer, clz));
}
writeFieldValue.add(
serializeForNotNull(
fieldValue, buffer, descriptor.getTypeToken()));
}
return new If(
ExpressionUtils.not(writeRefOrNull(buffer, fieldValue)),
writeFieldValue);
},
"writeField",
fieldInfo.getEncodedFieldInfo());
walkPath.removeLast();
groupExpressions.add(writeFieldAction);
}
return groupExpressions;
},
"writeSeparateTypesHashFields");
expressions.add(invokeGeneratedWrite);
});
expressions.add(
new Invoke(
buffer, "writeInt64", new Literal(fieldResolver.getEndTag(), PRIMITIVE_LONG_TYPE)));
return expressions;
}
private Expression writeEmbedTypeFieldValue(
Expression bean, Expression buffer, FieldInfo fieldInfo) {
Descriptor descriptor = createDescriptor(fieldInfo);
walkPath.add(descriptor.getDeclaringClass() + descriptor.getName());
Expression fieldValue = getFieldValue(bean, descriptor);
walkPath.removeLast();
return serializeFor(fieldValue, buffer, descriptor.getTypeToken());
}
@Override
public Expression buildDecodeExpression() {
if (isRecord) {
return buildRecordDecodeExpression();
}
Reference buffer = new Reference(BUFFER_NAME, bufferTypeToken, false);
ListExpression expressionBuilder = new ListExpression();
Expression bean = newBean();
Expression referenceObject = new Invoke(refResolverRef, "reference", PRIMITIVE_VOID_TYPE, bean);
expressionBuilder.add(bean);
expressionBuilder.add(referenceObject);
if (!GeneratedCompatibleSerializer.class.isAssignableFrom(parentSerializerClass)) {
expressionBuilder.add(readAndSetFields(buffer, bean));
} else {
// Use cast to make generated method signature match
// `CompatibleSerializerBase.readAndSetFields`
Expression target1 = new Cast(bean, OBJECT_TYPE, "bean", false, false);
Expression target2 = new Cast(target1, bean.type(), "bean", false, false);
ListExpression readAndSetFieldsExpr = readAndSetFields(buffer, target2);
readAndSetFieldsExpr.add(new Return(target2));
expressionBuilder.add(
ExpressionOptimizer.invokeGenerated(
ctx,
new LinkedHashSet<>(Arrays.asList(buffer, target1)),
readAndSetFieldsExpr,
"public",
"readAndSetFields",
false));
}
expressionBuilder.add(new Return(bean));
return expressionBuilder;
}
public Expression buildRecordDecodeExpression() {
Reference buffer = new Reference(BUFFER_NAME, bufferTypeToken, false);
StaticInvoke components =
new StaticInvoke(
Platform.class, "copyObjectArray", OBJECT_ARRAY_TYPE, recordComponentDefaultValues);
ListExpression readAndSetFieldsExpr = new ListExpression();
Expression partFieldInfo =
new Invoke(buffer, readIntFunc(), "partFieldInfo", PRIMITIVE_LONG_TYPE);
readAndSetFieldsExpr.add(partFieldInfo);
readEmbedTypes4Fields(buffer, readAndSetFieldsExpr, components, partFieldInfo);
BitOr newPartFieldInfo =
new BitOr(
new BitShift("<<", new Invoke(buffer, readIntFunc(), PRIMITIVE_LONG_TYPE), 32),
new BitAnd(partFieldInfo, new Literal(0x00000000ffffffffL, PRIMITIVE_LONG_TYPE)));
readAndSetFieldsExpr.add(new Assign(partFieldInfo, newPartFieldInfo));
readEmbedTypes9Fields(buffer, readAndSetFieldsExpr, components, partFieldInfo);
readEmbedTypesHashFields(buffer, readAndSetFieldsExpr, components, partFieldInfo);
readSeparateTypesHashFields(buffer, readAndSetFieldsExpr, components, partFieldInfo);
readAndSetFieldsExpr.add(new Return(components));
Expression readActions =
ExpressionOptimizer.invokeGenerated(
ctx,
new LinkedHashSet<>(Arrays.asList(buffer, components)),
readAndSetFieldsExpr.add(components),
"private",
"readFields",
false);
StaticInvoke record =
new StaticInvoke(
RecordUtils.class,
"invokeRecordCtrHandle",
OBJECT_TYPE,
getRecordCtrHandle(),
components);
return new ListExpression(buffer, components, readActions, new Return(record));
}
@Override
protected Expression setFieldValue(Expression bean, Descriptor d, Expression value) {
if (isRecord) {
int index = recordReversedMapping.get(d.getName());
return new AssignArrayElem(bean, value, Literal.ofInt(index));
}
return super.setFieldValue(bean, d, value);
}
private ListExpression readAndSetFields(Reference buffer, Expression bean) {
ListExpression readAndSetFieldsExpr = new ListExpression();
Expression partFieldInfo =
new Invoke(buffer, readIntFunc(), "partFieldInfo", PRIMITIVE_LONG_TYPE);
readAndSetFieldsExpr.add(partFieldInfo);
readEmbedTypes4Fields(buffer, readAndSetFieldsExpr, bean, partFieldInfo);
BitOr newPartFieldInfo =
new BitOr(
new BitShift("<<", new Invoke(buffer, readIntFunc(), PRIMITIVE_LONG_TYPE), 32),
new BitAnd(partFieldInfo, new Literal(0x00000000ffffffffL, PRIMITIVE_LONG_TYPE)));
readAndSetFieldsExpr.add(new Assign(partFieldInfo, newPartFieldInfo));
readEmbedTypes9Fields(buffer, readAndSetFieldsExpr, bean, partFieldInfo);
readEmbedTypesHashFields(buffer, readAndSetFieldsExpr, bean, partFieldInfo);
readSeparateTypesHashFields(buffer, readAndSetFieldsExpr, bean, partFieldInfo);
return readAndSetFieldsExpr;
}
private void readEmbedTypes4Fields(
Reference buffer,
ListExpression expressionBuilder,
Expression bean,
Expression partFieldInfo) {
FieldInfo[] embedTypes4Fields = fieldResolver.getEmbedTypes4Fields();
if (embedTypes4Fields.length > 0) {
long minFieldInfo = embedTypes4Fields[0].getEncodedFieldInfo();
expressionBuilder.add(skipDataBy4Until(bean, buffer, partFieldInfo, minFieldInfo, false));
groupFields(embedTypes4Fields, 3)
.forEach(
group -> {
Expression invokeGeneratedRead =
ExpressionOptimizer.invokeGenerated(
ctx,
() -> {
ListExpression groupExpressions = new ListExpression();
for (FieldInfo fieldInfo : group) {
long encodedFieldInfo = fieldInfo.getEncodedFieldInfo();
Descriptor descriptor = createDescriptor(fieldInfo);
walkPath.add(descriptor.getDeclaringClass() + descriptor.getName());
Expression readField =
readEmbedTypes4(bean, buffer, descriptor, partFieldInfo);
Expression tryReadField =
new ListExpression(
skipDataBy4Until(
bean, buffer, partFieldInfo, encodedFieldInfo, true),
new If(
eq(
partFieldInfo,
new Literal(encodedFieldInfo, PRIMITIVE_LONG_TYPE)),
readEmbedTypes4(bean, buffer, descriptor, partFieldInfo)));
groupExpressions.add(
new If(
eq(
partFieldInfo,
new Literal(encodedFieldInfo, PRIMITIVE_LONG_TYPE)),
readField,
tryReadField,
false,
PRIMITIVE_VOID_TYPE));
walkPath.removeLast();
}
groupExpressions.add(new Return(partFieldInfo));
return groupExpressions;
},
"readEmbedTypes4Fields",
true);
expressionBuilder.add(
new Assign(partFieldInfo, invokeGeneratedRead),
new If(eq(partFieldInfo, endTagLiteral), new Return(bean)));
});
}
expressionBuilder.add(skipField4End(bean, buffer, partFieldInfo));
}
private Expression readEmbedTypes4(
Expression bean, Expression buffer, Descriptor descriptor, Expression partFieldInfo) {
Expression deserializeAction =
deserializeFor(
buffer,
descriptor.getTypeToken(),
expr ->
setFieldValue(bean, descriptor, tryInlineCast(expr, descriptor.getTypeToken())));
return new ListExpression(
deserializeAction,
new Assign(partFieldInfo, inlineInvoke(buffer, readIntFunc(), PRIMITIVE_LONG_TYPE)));
}
/**
* Skip data whose field info is encoded as 4-bytes until specified field matched.
*
* <p>This is JIT-version of code:
*
* <pre>{@code
* while ((partFieldInfo & 0b11) == FieldResolver.EMBED_TYPES_4_FLAG
* && partFieldInfo < targetFieldInfo) {
* if (fieldResolver.skipDataBy4(buffer, (int) partFieldInfo)) {
* return bean;
* }
* partFieldInfo = buffer.readInt32();
* }
* }</pre>
*/
private Expression skipDataBy4Until(
Expression bean,
Expression buffer,
Expression partFieldInfo,
long targetFieldInfo,
boolean returnEndTag) {
Literal targetFieldInfoExpr = new Literal(targetFieldInfo, PRIMITIVE_LONG_TYPE);
Expression.LogicalAnd predicate =
new Expression.LogicalAnd(
isEmbedType(partFieldInfo, 0b11, FieldResolver.EMBED_TYPES_4_FLAG),
lessThan(partFieldInfo, targetFieldInfoExpr));
return new While(
predicate,
() ->
new ListExpression(
new If(
eq(
inlineInvoke(
fieldResolverRef,
"skipDataBy4",
PRIMITIVE_BOOLEAN_TYPE,
buffer,
cast(partFieldInfo, PRIMITIVE_INT_TYPE)),
endTagLiteral),
returnEndTag ? new Return(endTagLiteral) : new Return(bean)),
new Assign(
partFieldInfo, inlineInvoke(buffer, readIntFunc(), PRIMITIVE_LONG_TYPE))));
}
/**
* Skip tailing fields with 4-byte field info.
*
* <p>This is JIT-version of code:
*
* <pre>{@code
* while ((partFieldInfo & 0b11) == FieldResolver.EMBED_TYPES_4_FLAG) {
* if (fieldResolver.skipDataBy4(buffer, (int) partFieldInfo)) {
* return bean;
* }
* partFieldInfo = buffer.readInt32();
* }
* }</pre>
*/
private Expression skipField4End(Expression bean, Expression buffer, Expression partFieldInfo) {
return new While(
isEmbedType(partFieldInfo, 0b11, FieldResolver.EMBED_TYPES_4_FLAG),
() ->
new ListExpression(
new If(
eq(
inlineInvoke(
fieldResolverRef,
"skipDataBy4",
PRIMITIVE_BOOLEAN_TYPE,
buffer,
cast(partFieldInfo, PRIMITIVE_INT_TYPE)),
endTagLiteral),
new Return(bean)),
new Assign(
partFieldInfo, inlineInvoke(buffer, readIntFunc(), PRIMITIVE_LONG_TYPE))));
}
private void readEmbedTypes9Fields(
Expression buffer, ListExpression expressions, Expression bean, Expression partFieldInfo) {
FieldInfo[] embedTypes9Fields = fieldResolver.getEmbedTypes9Fields();
if (embedTypes9Fields.length > 0) {
long minFieldInfo = embedTypes9Fields[0].getEncodedFieldInfo();
expressions.add(
skipDataBy8Until(
bean,
buffer,
partFieldInfo,
minFieldInfo,
0b111,
FieldResolver.EMBED_TYPES_9_FLAG,
false));
groupFields(embedTypes9Fields, 3)
.forEach(
group -> {
Expression invokeGeneratedRead =
ExpressionOptimizer.invokeGenerated(
ctx,
() -> {
ListExpression groupExpressions = new ListExpression();
for (FieldInfo fieldInfo : group) {
groupExpressions.add(
processEmbedTypes8Field(
buffer,
bean,
partFieldInfo,
fieldInfo.getEncodedFieldInfo(),
FieldResolver.EMBED_TYPES_9_FLAG,
fieldInfo));
}
groupExpressions.add(new Return(partFieldInfo));
return groupExpressions;
},
"readEmbedTypes9Fields",
true);
expressions.add(
new Assign(partFieldInfo, invokeGeneratedRead),
new If(eq(partFieldInfo, endTagLiteral), new Return(bean)));
});
}
expressions.add(
skipField8End(bean, buffer, partFieldInfo, 0b111, FieldResolver.EMBED_TYPES_9_FLAG));
}
private void readEmbedTypesHashFields(
Expression buffer, ListExpression expressions, Expression bean, Expression partFieldInfo) {
FieldInfo[] embedTypesHashFields = fieldResolver.getEmbedTypesHashFields();
if (embedTypesHashFields.length > 0) {
long minFieldInfo = embedTypesHashFields[0].getEncodedFieldInfo();
expressions.add(
skipDataBy8Until(
bean,
buffer,
partFieldInfo,
minFieldInfo,
0b111,
FieldResolver.EMBED_TYPES_HASH_FLAG,
false));
groupFields(embedTypesHashFields, 3)
.forEach(
group -> {
Expression invokeGeneratedRead =
ExpressionOptimizer.invokeGenerated(
ctx,
() -> {
ListExpression groupExpressions = new ListExpression();
for (FieldInfo fieldInfo : group) {
groupExpressions.add(
processEmbedTypes8Field(
buffer,
bean,
partFieldInfo,
fieldInfo.getEncodedFieldInfo(),
FieldResolver.EMBED_TYPES_HASH_FLAG,
fieldInfo));
}
groupExpressions.add(new Return(partFieldInfo));
return groupExpressions;
},
"readEmbedTypesHashFields",
true);
expressions.add(
new Assign(partFieldInfo, invokeGeneratedRead),
new If(eq(partFieldInfo, endTagLiteral), new Return(bean)));
});
}
expressions.add(
skipField8End(bean, buffer, partFieldInfo, 0b111, FieldResolver.EMBED_TYPES_HASH_FLAG));
}
private Expression processEmbedTypes8Field(
Expression buffer,
Expression bean,
Expression partFieldInfo,
long targetFieldInfo,
byte flagValue,
FieldInfo fieldInfo) {
long encodedFieldInfo = fieldInfo.getEncodedFieldInfo();
Descriptor descriptor = createDescriptor(fieldInfo);
walkPath.add(descriptor.getDeclaringClass() + descriptor.getName());
Expression readField = readEmbedTypes8Field(bean, buffer, descriptor, partFieldInfo);
Expression tryReadField =
new ListExpression(
skipDataBy8Until(bean, buffer, partFieldInfo, targetFieldInfo, 0b111, flagValue, true),
new If(
eq(partFieldInfo, new Literal(encodedFieldInfo, PRIMITIVE_LONG_TYPE)),
readEmbedTypes8Field(bean, buffer, descriptor, partFieldInfo)));
walkPath.removeLast();
return new If(
eq(partFieldInfo, new Literal(encodedFieldInfo, PRIMITIVE_LONG_TYPE)),
readField,
tryReadField);
}
private Expression readEmbedTypes8Field(
Expression bean, Expression buffer, Descriptor descriptor, Expression partFieldInfo) {
Expression deserializeAction =
deserializeFor(
buffer,
descriptor.getTypeToken(),
expr ->
setFieldValue(bean, descriptor, tryInlineCast(expr, descriptor.getTypeToken())));
return new ListExpression(
deserializeAction,
new Assign(partFieldInfo, inlineInvoke(buffer, readLongFunc(), PRIMITIVE_LONG_TYPE)));
}
private void readSeparateTypesHashFields(
Expression buffer,
ListExpression expressionBuilder,
Expression bean,
Expression partFieldInfo) {
FieldInfo[] separateTypesHashFields = fieldResolver.getSeparateTypesHashFields();
if (separateTypesHashFields.length > 0) {
long minFieldInfo = separateTypesHashFields[0].getEncodedFieldInfo();
expressionBuilder.add(
skipDataBy8Until(
bean,
buffer,
partFieldInfo,
minFieldInfo,
0b11,
FieldResolver.SEPARATE_TYPES_HASH_FLAG,
false));
groupFields(separateTypesHashFields, 3)
.forEach(
group -> {
Expression invokeGeneratedRead =
ExpressionOptimizer.invokeGenerated(
ctx,
() -> {
ListExpression groupExpressions = new ListExpression();
for (FieldInfo fieldInfo : group) {
long encodedFieldInfo = fieldInfo.getEncodedFieldInfo();
Descriptor descriptor = createDescriptor(fieldInfo);
walkPath.add(descriptor.getDeclaringClass() + descriptor.getName());
Expression readField =
readObjectField(fieldInfo, bean, buffer, descriptor, partFieldInfo);
Expression tryReadField =
new ListExpression(
skipDataBy8Until(
bean,
buffer,
partFieldInfo,
encodedFieldInfo,
0b11,
FieldResolver.SEPARATE_TYPES_HASH_FLAG,
true),
new If(
eq(
partFieldInfo,
new Literal(encodedFieldInfo, PRIMITIVE_LONG_TYPE)),
readObjectField(
fieldInfo, bean, buffer, descriptor, partFieldInfo)));
walkPath.removeLast();
groupExpressions.add(
new If(
eq(
partFieldInfo,
new Literal(encodedFieldInfo, PRIMITIVE_LONG_TYPE)),
readField,
tryReadField));
}
groupExpressions.add(new Return(partFieldInfo));
return groupExpressions;
},
"readSeparateTypesHashFields",
true);
expressionBuilder.add(
new Assign(partFieldInfo, invokeGeneratedRead),
new If(eq(partFieldInfo, endTagLiteral), new Return(bean)));
});
}
expressionBuilder.add(new Invoke(fieldResolverRef, "skipEndFields", buffer, partFieldInfo));
}
private Expression readObjectField(
FieldInfo fieldInfo,
Expression bean,
Expression buffer,
Descriptor descriptor,
Expression partFieldInfo) {
Expression readAction =
invokeGenerated(
ctx,
() -> {
TypeToken<?> typeToken = descriptor.getTypeToken();
Expression refId = tryPreserveRefId(buffer);
// indicates that the object is first read.
Expression needDeserialize =
ExpressionUtils.egt(
refId, new Literal(Fury.NOT_NULL_VALUE_FLAG, PRIMITIVE_BYTE_TYPE));
byte type = fieldInfo.getFieldType();
// check `FieldTypes` byte value.
Expression expectType = new Literal(type, PRIMITIVE_BYTE_TYPE);
ListExpression deserializedValue =
new ListExpression(
new Invoke(
fieldResolverRef,
"checkFieldType",
inlineInvoke(buffer, "readByte", PRIMITIVE_BYTE_TYPE),
expectType));
if (type == FieldTypes.OBJECT) {
deserializedValue.add(readForNotNullNonFinal(buffer, typeToken, null));
} else {
if (type == FieldTypes.COLLECTION_ELEMENT_FINAL) {
deserializedValue.add(
skipFinalClassInfo(
((CollectionFieldInfo) fieldInfo).getElementType(), buffer));
} else if (type == FieldTypes.MAP_KV_FINAL) {
deserializedValue.add(
skipFinalClassInfo(((MapFieldInfo) fieldInfo).getKeyType(), buffer));
deserializedValue.add(
skipFinalClassInfo(((MapFieldInfo) fieldInfo).getValueType(), buffer));
} else if (type == FieldTypes.MAP_KEY_FINAL) {
deserializedValue.add(
skipFinalClassInfo(((MapFieldInfo) fieldInfo).getKeyType(), buffer));
} else {
Preconditions.checkArgument(type == FieldTypes.MAP_VALUE_FINAL, fieldInfo);
deserializedValue.add(
skipFinalClassInfo(((MapFieldInfo) fieldInfo).getValueType(), buffer));
}
Class<?> clz = getRawType(typeToken);
if (ReflectionUtils.isMonomorphic(clz)) {
// deserializeForNotNull won't read field type if it's final
deserializedValue.add(skipFinalClassInfo(clz, buffer));
}
deserializedValue.add(deserializeForNotNull(buffer, typeToken, null));
}
Expression setReadObject =
new Invoke(refResolverRef, "setReadObject", refId, deserializedValue);
// use false to ignore null
return new If(
needDeserialize,
new ListExpression(
refId,
deserializedValue,
setReadObject,
setFieldValue(bean, descriptor, tryInlineCast(deserializedValue, typeToken))),
setFieldValue(
bean,
descriptor,
tryInlineCast(
new Invoke(refResolverRef, "getReadObject", OBJECT_TYPE, false),
typeToken)),
false,
PRIMITIVE_VOID_TYPE);
},
"readField",
fieldInfo.getEncodedFieldInfo());
return new ListExpression(
new Expression.ForceEvaluate(readAction),
new Assign(partFieldInfo, inlineInvoke(buffer, readLongFunc(), PRIMITIVE_LONG_TYPE)));
}
protected Expression getFinalClassInfo(Class<?> cls) {
Preconditions.checkArgument(ReflectionUtils.isMonomorphic(cls));
Tuple2<Reference, Boolean> classInfoRef = addClassInfoField(cls);
Preconditions.checkArgument(!classInfoRef.f1);
return classInfoRef.f0;
}
protected Expression writeFinalClassInfo(Expression buffer, Class<?> cls) {
Preconditions.checkArgument(ReflectionUtils.isMonomorphic(cls));
ClassInfo classInfo = visitFury(f -> f.getClassResolver().getClassInfo(cls, false));
if (classInfo != null && classInfo.getClassId() != ClassResolver.NO_CLASS_ID) {
return fury.getClassResolver().writeClassExpr(buffer, classInfo.getClassId());
}
Expression classInfoExpr = getFinalClassInfo(cls);
return new Invoke(classResolverRef, "writeClass", buffer, classInfoExpr);
}
protected Expression skipFinalClassInfo(Class<?> cls, Expression buffer) {
Preconditions.checkArgument(ReflectionUtils.isMonomorphic(cls));
ClassInfo classInfo = visitFury(f -> f.getClassResolver().getClassInfo(cls, false));
if (classInfo != null && classInfo.getClassId() != ClassResolver.NO_CLASS_ID) {
return fury.getClassResolver().skipRegisteredClassExpr(buffer);
}
// read `ClassInfo` is not used, set `inlineReadClassInfo` false,
// to avoid read doesn't happen in generated code.
return readClassInfo(cls, buffer, false);
}
/**
* Skip data whose field info is encoded as 8-bytes until specified field matched.
*
* <p>This is JIT-version of code:
*
* <pre>{@code
* while ((partFieldInfo & flagBits) == flagValue
* && partFieldInfo < targetFieldInfo) {
* if (fieldResolver.skipDataBy8(buffer, partFieldInfo)) {
* return bean;
* }
* partFieldInfo = buffer.readInt64();
* }
* }</pre>
*/
private Expression skipDataBy8Until(
Expression bean,
Expression buffer,
Expression partFieldInfo,
long targetFieldInfo,
int flagBits,
byte flagValue,
boolean returnEndTag) {
Literal targetFieldInfoExpr = new Literal(targetFieldInfo, PRIMITIVE_LONG_TYPE);
Expression.LogicalAnd predicate =
new Expression.LogicalAnd(
isEmbedType(partFieldInfo, flagBits, flagValue),
lessThan(partFieldInfo, targetFieldInfoExpr));
return new While(
predicate,
() ->
new ListExpression(
new If(
eq(
inlineInvoke(
fieldResolverRef,
"skipDataBy8",
PRIMITIVE_BOOLEAN_TYPE,
buffer,
partFieldInfo),
endTagLiteral),
returnEndTag ? new Return(endTagLiteral) : new Return(bean)),
new Assign(
partFieldInfo, inlineInvoke(buffer, readLongFunc(), PRIMITIVE_LONG_TYPE))));
}
private Expression skipField8End(
Expression bean, Expression buffer, Expression partFieldInfo, int flagBits, byte flagValue) {
return new While(
isEmbedType(partFieldInfo, flagBits, flagValue),
() ->
new ListExpression(
new If(
eq(
inlineInvoke(
fieldResolverRef,
"skipDataBy8",
PRIMITIVE_BOOLEAN_TYPE,
buffer,
partFieldInfo),
endTagLiteral),
new Return(bean)),
new Assign(
partFieldInfo, inlineInvoke(buffer, readLongFunc(), PRIMITIVE_LONG_TYPE))));
}
private Comparator isEmbedType(Expression partFieldInfo, int flagBits, byte flagValue) {
return eq(
new BitAnd(partFieldInfo, new Literal(flagBits, PRIMITIVE_LONG_TYPE)),
new Literal(flagValue, PRIMITIVE_BYTE_TYPE));
}
private static List<List<FieldInfo>> groupFields(FieldInfo[] fieldsInfo, int groupSize) {
List<List<FieldInfo>> groups = new ArrayList<>();
List<FieldInfo> group = new ArrayList<>();
for (FieldInfo fieldInfo : fieldsInfo) {
if (group.size() == groupSize) {
groups.add(group);
group = new ArrayList<>();
}
group.add(fieldInfo);
}
if (!group.isEmpty()) {
groups.add(group);
}
return groups;
}
}