mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-10-03 00:44:52 +00:00
GP-3288 add support for Golang 1.17
Also fix error with structure name tagging introduced in previous commit
This commit is contained in:
parent
536739f0a5
commit
d3c3287eb2
|
@ -86,6 +86,7 @@ data/symbols/win64/mfc90u.exports||GHIDRA||||END|
|
|||
data/symbols/win64/msvcrt.hints||GHIDRA||||END|
|
||||
data/typeinfo/generic/generic_clib.gdt||GHIDRA||||END|
|
||||
data/typeinfo/generic/generic_clib_64.gdt||GHIDRA||||END|
|
||||
data/typeinfo/golang/golang_1.17_anybit_any.gdt||GHIDRA||||END|
|
||||
data/typeinfo/golang/golang_1.18_anybit_any.gdt||GHIDRA||||END|
|
||||
data/typeinfo/mac_10.9/mac_osx.gdt||GHIDRA||||END|
|
||||
data/typeinfo/win32/msvcrt/clsids.txt||GHIDRA||reviewed||END|
|
||||
|
|
Binary file not shown.
|
@ -343,8 +343,8 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
|||
* <p>
|
||||
* The zerobase symbol is used as the location of parameters that are zero-length.
|
||||
*
|
||||
* @param prog
|
||||
* @return
|
||||
* @param prog {@link Program}
|
||||
* @return {@link Address} of the runtime.zerobase, or artificial substitute
|
||||
*/
|
||||
public static Address getZerobaseAddress(Program prog) {
|
||||
Symbol zerobaseSym = SymbolUtilities.getUniqueSymbol(prog, "runtime.zerobase");
|
||||
|
|
|
@ -17,7 +17,6 @@ package ghidra.app.util.bin.format.golang.rtti;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.symbol.SymbolUtilities;
|
||||
|
@ -31,27 +30,51 @@ public class GoFuncData implements StructureMarkup<GoFuncData> {
|
|||
@ContextField
|
||||
private StructureContext<GoFuncData> context;
|
||||
|
||||
@FieldMapping
|
||||
@FieldMapping(optional = true)
|
||||
@EOLComment("description")
|
||||
@MarkupReference("funcAddress")
|
||||
private long entryoff;
|
||||
private long entryoff; // valid in >=1.18, relative offset of function
|
||||
|
||||
@FieldMapping(optional = true)
|
||||
@EOLComment("description")
|
||||
@MarkupReference("funcAddress")
|
||||
private long entry; // valid in <=1.17, location of function
|
||||
|
||||
@FieldMapping
|
||||
@MarkupReference("nameAddress")
|
||||
private long nameoff;
|
||||
|
||||
private Address funcAddress; // set when entryoff or entry are set
|
||||
|
||||
public void setEntryoff(long entryoff) {
|
||||
this.entryoff = entryoff;
|
||||
|
||||
GoModuledata moduledata = getModuledata();
|
||||
this.funcAddress = moduledata != null ? moduledata.getText().add(entryoff) : null;
|
||||
}
|
||||
|
||||
public void setEntry(long entry) {
|
||||
this.entry = entry;
|
||||
this.funcAddress = context.getDataTypeMapper().getCodeAddress(entry);
|
||||
}
|
||||
|
||||
public Address getFuncAddress() {
|
||||
return getModuledata().getText().add(entryoff);
|
||||
return funcAddress;
|
||||
}
|
||||
|
||||
public Address getNameAddress() {
|
||||
return getModuledata().getFuncnametab().getArrayAddress().add(nameoff);
|
||||
GoModuledata moduledata = getModuledata();
|
||||
return moduledata != null
|
||||
? moduledata.getFuncnametab().getArrayAddress().add(nameoff)
|
||||
: null;
|
||||
}
|
||||
|
||||
public String getName() throws IOException {
|
||||
BinaryReader reader =
|
||||
programContext.getReader(getModuledata().getFuncnametab().getArrayOffset() + nameoff);
|
||||
return reader.readNextUtf8String();
|
||||
GoModuledata moduledata = getModuledata();
|
||||
return moduledata != null
|
||||
? programContext.getReader(moduledata.getFuncnametab().getArrayOffset() + nameoff)
|
||||
.readNextUtf8String()
|
||||
: null;
|
||||
}
|
||||
|
||||
public String getDescription() throws IOException {
|
||||
|
|
|
@ -28,21 +28,42 @@ public class GoFunctabEntry {
|
|||
@ContextField
|
||||
private StructureContext<GoFunctabEntry> context;
|
||||
|
||||
@FieldMapping
|
||||
@FieldMapping(optional = true)
|
||||
@MarkupReference("funcAddress")
|
||||
private long entryoff; // offset relative to module's .text
|
||||
private long entryoff; // valid in >=1.18, relative offset of function
|
||||
|
||||
@FieldMapping(optional = true)
|
||||
@MarkupReference("funcAddress")
|
||||
private long entry; // valid in <=1.17, location of function
|
||||
|
||||
@FieldMapping
|
||||
@MarkupReference("funcData")
|
||||
private long funcoff; // offset into pclntable -> _func
|
||||
|
||||
private Address funcAddress;
|
||||
|
||||
public void setEntryoff(long entryoff) {
|
||||
this.entryoff = entryoff;
|
||||
|
||||
GoModuledata moduledata = getModuledata();
|
||||
this.funcAddress = moduledata != null ? moduledata.getText().add(entryoff) : null;
|
||||
}
|
||||
|
||||
public void setEntry(long entry) {
|
||||
this.entry = entry;
|
||||
this.funcAddress = programContext.getCodeAddress(entry);
|
||||
}
|
||||
|
||||
public Address getFuncAddress() {
|
||||
return getModuledata().getText().add(entryoff);
|
||||
return funcAddress;
|
||||
}
|
||||
|
||||
@Markup
|
||||
public GoFuncData getFuncData() throws IOException {
|
||||
return funcoff != 0 ? getModuledata().getFuncDataInstance(funcoff) : null;
|
||||
GoModuledata moduledata = getModuledata();
|
||||
return funcoff != 0 && moduledata != null
|
||||
? moduledata.getFuncDataInstance(funcoff)
|
||||
: null;
|
||||
}
|
||||
|
||||
private GoModuledata getModuledata() {
|
||||
|
|
|
@ -24,8 +24,7 @@ import ghidra.app.util.bin.format.golang.rtti.types.GoType;
|
|||
import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.StringUTF8DataType;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
|
@ -85,6 +84,9 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
|||
@FieldMapping
|
||||
private GoSlice itablinks; // []*runtime.itab (array of pointers to runtime.tab)
|
||||
|
||||
@FieldMapping
|
||||
private GoSlice textsectmap; // []runtime.textsect, symbol runtime.textsectionmap also points
|
||||
|
||||
public GoModuledata() {
|
||||
// empty
|
||||
}
|
||||
|
@ -97,7 +99,7 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
|||
* @return boolean true if match, false if no match
|
||||
*/
|
||||
public boolean matchesPclntab(GoPcHeader pclntab) {
|
||||
return pclntab.getTextStart().equals(getText()) &&
|
||||
return (!pclntab.hasTextStart() || pclntab.getTextStart().equals(getText())) &&
|
||||
pclntab.getFuncnameAddress().equals(funcnametab.getArrayAddress());
|
||||
}
|
||||
|
||||
|
@ -163,7 +165,6 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
|||
|
||||
itablinks.markupArray("moduledata.itablinks", GoItab.class, true, session);
|
||||
|
||||
//cutab.markupArray("moduledata.cutab", dataTypeMapper.getUint32DT(), false);
|
||||
markupStringTable(funcnametab.getArrayAddress(), funcnametab.getLen(), session);
|
||||
markupStringTable(filetab.getArrayAddress(), filetab.getLen(), session);
|
||||
|
||||
|
@ -176,6 +177,12 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
|||
subSlice.markupArray("moduledata.ftab", GoFunctabEntry.class, false, session);
|
||||
subSlice.markupArrayElements(GoFunctabEntry.class, session);
|
||||
}
|
||||
|
||||
Structure textsectDT =
|
||||
programContext.getGhidraDataType("runtime.textsect", Structure.class);
|
||||
if (textsectDT != null) {
|
||||
textsectmap.markupArray("runtime.textsectionmap", textsectDT, false, session);
|
||||
}
|
||||
}
|
||||
|
||||
@Markup
|
||||
|
|
|
@ -112,7 +112,7 @@ public class GoPcHeader {
|
|||
Memory memory = programContext.getProgram().getMemory();
|
||||
Address pclntabAddr =
|
||||
memory.findBytes(range.getMinAddress(), range.getMaxAddress(), searchBytes, searchMask,
|
||||
true, TaskMonitor.DUMMY);
|
||||
true, monitor);
|
||||
if (pclntabAddr == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ public class GoPcHeader {
|
|||
@FieldMapping
|
||||
private byte ptrSize;
|
||||
|
||||
@FieldMapping
|
||||
@FieldMapping(optional = true) // present >= 1.18
|
||||
@MarkupReference
|
||||
private long textStart; // should be same as offset of ".text"
|
||||
|
||||
|
@ -190,6 +190,10 @@ public class GoPcHeader {
|
|||
return ver;
|
||||
}
|
||||
|
||||
public boolean hasTextStart() {
|
||||
return textStart != 0;
|
||||
}
|
||||
|
||||
public Address getTextStart() {
|
||||
return programContext.getDataAddress(textStart);
|
||||
}
|
||||
|
|
|
@ -44,16 +44,17 @@ public class GoSlice {
|
|||
private long cap;
|
||||
|
||||
public GoSlice() {
|
||||
// emtpy
|
||||
}
|
||||
|
||||
public GoSlice(long array, long len, long cap) {
|
||||
this.array = array;
|
||||
this.len = len;
|
||||
this.cap = cap;
|
||||
this(array, len, cap, null);
|
||||
}
|
||||
|
||||
public GoSlice(long array, long len, long cap, GoRttiMapper programContext) {
|
||||
this(array, len, cap);
|
||||
this.array = array;
|
||||
this.len = len;
|
||||
this.cap = cap;
|
||||
this.programContext = programContext;
|
||||
}
|
||||
|
||||
|
@ -69,6 +70,10 @@ public class GoSlice {
|
|||
return new GoSlice(array + (startElement * elementSize), elementCount, elementCount, programContext);
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return array != 0 && isValid(1);
|
||||
}
|
||||
|
||||
public boolean isValid(int elementSize) {
|
||||
try {
|
||||
Memory memory = programContext.getProgram().getMemory();
|
||||
|
@ -191,7 +196,7 @@ public class GoSlice {
|
|||
*/
|
||||
public void markupArray(String sliceName, DataType elementType, boolean ptr,
|
||||
MarkupSession session) throws IOException {
|
||||
if (len == 0) {
|
||||
if (len == 0 || !isValid()) {
|
||||
return;
|
||||
}
|
||||
DataTypeManager dtm = programContext.getDTM();
|
||||
|
|
|
@ -41,14 +41,41 @@ import java.lang.annotation.Target;
|
|||
@Target(FIELD)
|
||||
public @interface FieldMapping {
|
||||
/**
|
||||
* Overrides the field name that is matched in the structure
|
||||
* Overrides the field name that is matched in the structure.
|
||||
* <p>
|
||||
* Can be a single name, or a list of names that will be used to find the structure
|
||||
* field.
|
||||
*
|
||||
* @return name of the structure field to map, or unset to use the java field's name
|
||||
* @return name, or list of names, of the structure field to map, or unset to use the
|
||||
* java field's name
|
||||
*/
|
||||
String fieldName() default "";
|
||||
String[] fieldName() default "";
|
||||
|
||||
/**
|
||||
* Optional function that will deserialize the tagged field
|
||||
* Marks this field as optional.
|
||||
* <p>
|
||||
* When marked optional, if a binding between the tagged java field and a structure field is
|
||||
* not successfully found, this field definition will be skipped.
|
||||
*
|
||||
* @return boolean flag, if true this field is optional, if false or unset, the field is
|
||||
* required
|
||||
*/
|
||||
boolean optional() default false;
|
||||
|
||||
/**
|
||||
* Specifies the name of a setter method that will be used to assign the deserialized value
|
||||
* to the java field.
|
||||
* <p>
|
||||
* If unset, a "void setFieldname(field_type)" method will be searched for.
|
||||
* <p>
|
||||
* If no setter method is present, the field's value will be directly assigned.
|
||||
*
|
||||
* @return optional name of a setter method
|
||||
*/
|
||||
String setter() default "";
|
||||
|
||||
/**
|
||||
* Optional function that will deserialize the tagged field.
|
||||
*
|
||||
* @return {@link FieldReadFunction}
|
||||
*/
|
||||
|
|
|
@ -77,6 +77,7 @@ public class FieldMappingInfo<T> {
|
|||
private final List<FieldMarkupFunction<T>> markupFuncs = new ArrayList<>();
|
||||
|
||||
private FieldReadFunction<T> readerFunc;
|
||||
private Method setterMethod;
|
||||
|
||||
private FieldMappingInfo(Field field, String dtcFieldName, DataTypeComponent dtc,
|
||||
Signedness signedness, int length) {
|
||||
|
@ -198,13 +199,24 @@ public class FieldMappingInfo<T> {
|
|||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void setReadFuncClass(Class<? extends FieldReadFunction> readFuncClass) {
|
||||
if (readFuncClass != FieldReadFunction.class) {
|
||||
this.readerFunc = ReflectionHelper.createInstance(readFuncClass, this);
|
||||
public void setFieldValueDeserializationInfo(Class<? extends FieldReadFunction> fieldReadValueClass,
|
||||
Class<?> structTargetClass, String setterNameOverride) {
|
||||
// setup the logic for assigning the deserialized value to the java field
|
||||
Class<?> fieldType = field.getType();
|
||||
|
||||
// TODO: be more strict about setter name, if specified it must be found or error
|
||||
Method setterMethod = ReflectionHelper.findSetter(field.getName(), setterNameOverride,
|
||||
structTargetClass, fieldType);
|
||||
if (setterMethod != null) {
|
||||
this.setterMethod = setterMethod;
|
||||
}
|
||||
|
||||
// setup the logic for deserializing the value from the in-memory structure
|
||||
if (fieldReadValueClass != FieldReadFunction.class) {
|
||||
this.readerFunc = ReflectionHelper.createInstance(fieldReadValueClass, this);
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?> fieldType = field.getType();
|
||||
if (ReflectionHelper.isPrimitiveType(fieldType)) {
|
||||
this.readerFunc = getReadPrimitiveValueFunc(fieldType);
|
||||
return;
|
||||
|
@ -217,6 +229,16 @@ public class FieldMappingInfo<T> {
|
|||
|
||||
}
|
||||
|
||||
public void assignField(FieldContext<T> fieldContext, Object value) throws IOException {
|
||||
T structureInstance = fieldContext.getStructureInstance();
|
||||
if (setterMethod != null) {
|
||||
ReflectionHelper.callSetter(structureInstance, setterMethod, value);
|
||||
}
|
||||
else {
|
||||
ReflectionHelper.assignField(field, structureInstance, value);
|
||||
}
|
||||
}
|
||||
|
||||
private FieldReadFunction<T> getReadPrimitiveValueFunc(Class<?> destClass) {
|
||||
// Create a lambda that reads a primitive value from a context that is specific to the
|
||||
// java field's type (destClass)
|
||||
|
|
|
@ -268,7 +268,7 @@ public class MarkupSession {
|
|||
|
||||
if (instance instanceof StructureMarkup<?> sm) {
|
||||
String structureLabel = sm.getStructureLabel();
|
||||
if (structureLabel != null && structureLabel.isBlank()) {
|
||||
if (structureLabel != null && !structureLabel.isBlank()) {
|
||||
labelAddress(addr, structureLabel);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,11 +51,26 @@ public class ReflectionHelper {
|
|||
Map.entry(Byte.TYPE, "byte"),
|
||||
Map.entry(Character.class, "wchar"),
|
||||
Map.entry(Character.TYPE, "wchar"));
|
||||
private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_CLASSES =
|
||||
Map.ofEntries(
|
||||
Map.entry(Long.TYPE, Long.class),
|
||||
Map.entry(Integer.TYPE, Integer.class),
|
||||
Map.entry(Short.TYPE, Short.class),
|
||||
Map.entry(Byte.TYPE, Byte.class),
|
||||
Map.entry(Character.TYPE, Character.class));
|
||||
|
||||
public static boolean isPrimitiveType(Class<?> clazz) {
|
||||
return NUM_CLASSES.contains(clazz);
|
||||
}
|
||||
|
||||
public static Class<?> getPrimitiveWrapper(Class<?> primitiveType) {
|
||||
Class<?> wrapperClass = PRIMITIVE_WRAPPER_CLASSES.get(primitiveType);
|
||||
if (wrapperClass == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return wrapperClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a value to a field in a java class.
|
||||
*
|
||||
|
@ -198,6 +213,17 @@ public class ReflectionHelper {
|
|||
return getter;
|
||||
}
|
||||
|
||||
public static Method findSetter(String fieldName, String setterNameOverride,
|
||||
Class<?> structClass, Class<?> valueClass) {
|
||||
Method setter = getMethod(structClass, setterNameOverride, valueClass);
|
||||
if (setter == null) {
|
||||
String setSetterName = "set%s%s".formatted(fieldName.substring(0, 1).toUpperCase(),
|
||||
fieldName.substring(1));
|
||||
setter = getMethod(structClass, setSetterName, valueClass);
|
||||
}
|
||||
return setter;
|
||||
}
|
||||
|
||||
public static <T> Constructor<T> getCtor(Class<T> clazz, Class<?>... paramTypes) {
|
||||
try {
|
||||
return clazz.getDeclaredConstructor(paramTypes);
|
||||
|
@ -209,18 +235,20 @@ public class ReflectionHelper {
|
|||
}
|
||||
|
||||
static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
|
||||
try {
|
||||
// try both public and private methods in the class
|
||||
return clazz.getDeclaredMethod(methodName, paramTypes);
|
||||
}
|
||||
catch (NoSuchMethodException | SecurityException e) {
|
||||
// fail, next try inherited public methods
|
||||
}
|
||||
try {
|
||||
return clazz.getMethod(methodName, paramTypes);
|
||||
}
|
||||
catch (NoSuchMethodException | SecurityException e) {
|
||||
// fail
|
||||
if (methodName != null && !methodName.isBlank()) {
|
||||
try {
|
||||
// try both public and private methods in the class
|
||||
return clazz.getDeclaredMethod(methodName, paramTypes);
|
||||
}
|
||||
catch (NoSuchMethodException | SecurityException e) {
|
||||
// fail, next try inherited public methods
|
||||
}
|
||||
try {
|
||||
return clazz.getMethod(methodName, paramTypes);
|
||||
}
|
||||
catch (NoSuchMethodException | SecurityException e) {
|
||||
// fail
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -303,6 +331,15 @@ public class ReflectionHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static <T> void callSetter(Object obj, Method setterMethod, T value) throws IOException {
|
||||
try {
|
||||
setterMethod.invoke(obj, value);
|
||||
}
|
||||
catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of methods that have been marked with a specific annotation.
|
||||
*
|
||||
|
|
|
@ -163,7 +163,7 @@ public class StructureMappingInfo<T> {
|
|||
throw new IOException("Missing read info for field: " + fieldInfo.getField());
|
||||
}
|
||||
Object value = readFunc.get(fieldReadContext);
|
||||
assignField(fieldReadContext, value);
|
||||
fieldInfo.assignField(fieldReadContext, value);
|
||||
}
|
||||
context.reader.setPointerIndex(context.getStructureEnd());
|
||||
}
|
||||
|
@ -271,14 +271,6 @@ public class StructureMappingInfo<T> {
|
|||
return null;
|
||||
}
|
||||
|
||||
private void assignField(FieldContext<T> fieldContext, Object value)
|
||||
throws IOException {
|
||||
Field field = fieldContext.fieldInfo().getField();
|
||||
T structureInstance = fieldContext.getStructureInstance();
|
||||
|
||||
ReflectionHelper.assignField(field, structureInstance, value);
|
||||
}
|
||||
|
||||
private void readFieldInfo(Class<?> clazz) {
|
||||
Class<?> superclass = clazz.getSuperclass();
|
||||
if (superclass != null) {
|
||||
|
@ -291,6 +283,10 @@ public class StructureMappingInfo<T> {
|
|||
FieldOutput foa = field.getAnnotation(FieldOutput.class);
|
||||
if (fma != null || foa != null) {
|
||||
FieldMappingInfo<T> fmi = readFieldMappingInfo(field, fma);
|
||||
if (fmi == null) {
|
||||
// was marked optional field, just skip
|
||||
continue;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
fields.add(fmi);
|
||||
|
||||
|
@ -314,22 +310,26 @@ public class StructureMappingInfo<T> {
|
|||
}
|
||||
|
||||
private FieldMappingInfo<T> readFieldMappingInfo(Field field, FieldMapping fma) {
|
||||
String fieldName = fma != null && !fma.fieldName().isBlank()
|
||||
? fma.fieldName()
|
||||
: field.getName();
|
||||
DataTypeComponent dtc = getField(fieldName);
|
||||
String[] fieldNames = getFieldNamesToSearchFor(field, fma);
|
||||
DataTypeComponent dtc = getFirstMatchingField(fieldNames);
|
||||
if (useFieldMappingInfo && dtc == null) {
|
||||
if (fma.optional()) {
|
||||
return null;
|
||||
}
|
||||
throw new IllegalArgumentException("Missing structure field: %s in %s"
|
||||
.formatted(fieldName, targetClass.getSimpleName()));
|
||||
.formatted(Arrays.toString(fieldNames), targetClass.getSimpleName()));
|
||||
}
|
||||
|
||||
Signedness signedness = fma != null ? fma.signedness() : Signedness.Unspecified;
|
||||
int length = fma != null ? fma.length() : -1;
|
||||
FieldMappingInfo<T> fmi = useFieldMappingInfo
|
||||
? FieldMappingInfo.createEarlyBinding(field, dtc, signedness, length)
|
||||
: FieldMappingInfo.createLateBinding(field, fieldName, signedness, length);
|
||||
: FieldMappingInfo.createLateBinding(field, fieldNames[0], signedness, length);
|
||||
|
||||
fmi.setReadFuncClass(fma != null ? fma.readFunc() : FieldReadFunction.class);
|
||||
Class<? extends FieldReadFunction> fieldReadFuncClass =
|
||||
fma != null ? fma.readFunc() : FieldReadFunction.class;
|
||||
String setterNameOverride = fma != null ? fma.setter() : null;
|
||||
fmi.setFieldValueDeserializationInfo(fieldReadFuncClass, targetClass, setterNameOverride);
|
||||
fmi.addMarkupNestedFuncs();
|
||||
fmi.addCommentMarkupFuncs();
|
||||
fmi.addMarkupReferenceFunc();
|
||||
|
@ -337,6 +337,23 @@ public class StructureMappingInfo<T> {
|
|||
return fmi;
|
||||
}
|
||||
|
||||
private String[] getFieldNamesToSearchFor(Field field, FieldMapping fma) {
|
||||
String[] fmaFieldNames = fma != null ? fma.fieldName() : null;
|
||||
return fmaFieldNames != null && fmaFieldNames.length != 0 && !fmaFieldNames[0].isBlank()
|
||||
? fmaFieldNames
|
||||
: new String[] { field.getName() };
|
||||
}
|
||||
|
||||
private DataTypeComponent getFirstMatchingField(String[] fieldNames) {
|
||||
for (String fieldName : fieldNames) {
|
||||
DataTypeComponent dtc = getField(fieldName);
|
||||
if (dtc != null) {
|
||||
return dtc;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private FieldOutputInfo<T> readFieldOutputInfo(FieldMappingInfo<T> fmi, FieldOutput foa) {
|
||||
FieldOutputInfo<T> foi = new FieldOutputInfo<>(fmi, foa.dataTypeName(),
|
||||
foa.isVariableLength(), foa.ordinal(), foa.offset());
|
||||
|
|
Loading…
Reference in a new issue