private void ImplementUnionGetMaxSizeMethod(UnionTypeModel unionModel) { List <string> switchCases = new List <string>(); for (int i = 0; i < unionModel.UnionElementTypeModel.Length; ++i) { var unionMember = unionModel.UnionElementTypeModel[i]; int unionIndex = i + 1; string @case = $@" case {unionIndex}: return {this.InvokeGetMaxSizeMethod(unionMember, $"item.Item{unionIndex}")};"; switchCases.Add(@case); } string discriminatorPropertyName = nameof(FlatBufferUnion <int, int> .Discriminator); string body = $@" switch (item.{discriminatorPropertyName}) {{ {string.Join("\r\n", switchCases)} default: throw new System.InvalidOperationException(""Exception determining type of union. Discriminator = "" + item.{discriminatorPropertyName}); }} "; this.GenerateGetMaxSizeMethod(unionModel.ClrType, body); }
/// <summary> /// Generates a special property getter for union types. This stems from /// the fact that unions occupy two spots in the table's vtable to deserialize one /// logical field. This means that the logic to read them must also be special. /// </summary> private GeneratedProperty CreateUnionTableGetter(TableMemberModel memberModel, int index) { Type propertyType = memberModel.ItemTypeModel.ClrType; string defaultValue = CSharpHelpers.GetDefaultValueToken(memberModel); UnionTypeModel unionModel = (UnionTypeModel)memberModel.ItemTypeModel; GeneratedProperty generatedProperty = new GeneratedProperty(this.options, index, memberModel.PropertyInfo); // Start by generating switch cases. The codegen'ed union types have // well-defined constructors for each constituent type, so this .ctor // will always be available. List <string> switchCases = new List <string>(); for (int i = 0; i < unionModel.UnionElementTypeModel.Length; ++i) { var unionMember = unionModel.UnionElementTypeModel[i]; int unionIndex = i + 1; string structOffsetAdjustment = string.Empty; if (unionMember.SchemaType == FlatBufferSchemaType.Struct) { structOffsetAdjustment = $"offsetLocation += buffer.{nameof(InputBuffer.ReadUOffset)}(offsetLocation);"; } string @case = $@" case {unionIndex}: {structOffsetAdjustment} return new {CSharpHelpers.GetCompilableTypeName(unionModel.ClrType)}({this.GetReadInvocation(unionMember.ClrType, "buffer", "offsetLocation")}); "; switchCases.Add(@case); } generatedProperty.ReadValueMethodDefinition = $@" [MethodImpl(MethodImplOptions.AggressiveInlining)] private static {CSharpHelpers.GetCompilableTypeName(propertyType)} {generatedProperty.ReadValueMethodName}(InputBuffer buffer, int offset) {{ int discriminatorLocation = buffer.{nameof(InputBuffer.GetAbsoluteTableFieldLocation)}(offset, {index}); int offsetLocation = buffer.{nameof(InputBuffer.GetAbsoluteTableFieldLocation)}(offset, {index + 1}); if (discriminatorLocation == 0) {{ return {defaultValue}; }} else {{ byte discriminator = buffer.{nameof(InputBuffer.ReadByte)}(discriminatorLocation); if (discriminator == 0 && offsetLocation != 0) throw new System.IO.InvalidDataException(""FlatBuffer union had discriminator set but no offset.""); switch (discriminator) {{ {string.Join("\r\n", switchCases)} default: return {defaultValue}; }} }} }} "; return(generatedProperty); }
static public TSType NewType(XElement elem, TypeScriptDefContext context) { if (elem == null) return null; TSType T = null; UnionTypeModel TypeList = new UnionTypeModel(); HashSet<string> typesAlreadyInTheList = new HashSet<string>(); foreach (XElement e in elem.Elements()) { switch (e.Name.LocalName) { case "dotident": T = new BasicType(e); if (!Tool.IsBasicType(T)) T = (TSType)TypeDeclaration.New(e, null, context) ?? T; break; case "anonymoustype": T = new AnonymousType(e, context); break; case "functiontype": T = new FunctionType(e, context); break; case "generic": T = new Generic(e, context); break; case "arraylevel": T = new Model.Array(T); TypeList.Types.RemoveAt(TypeList.Types.Count - 1); break; default: continue; } if (!typesAlreadyInTheList.Contains(T.ToString())) // Avoids adding the same type multiple times { TypeList.Types.Add(T); typesAlreadyInTheList.Add(T.ToString()); } } if (TypeList.Count < 2) return T; else return TypeList; }
/// <summary> /// Tries to create a type model based on the given type. /// </summary> public bool TryCreateTypeModel(TypeModelContainer container, Type type, [NotNullWhen(true)] out ITypeModel?typeModel) { if (type == typeof(string)) { typeModel = new StringTypeModel(container); return(true); } if (type.IsArray) { if (typeof(IFlatBufferUnion).IsAssignableFrom(type.GetElementType())) { typeModel = new ArrayVectorOfUnionTypeModel(type, container); } else { typeModel = new ArrayVectorTypeModel(type, container); } return(true); } if (type.IsGenericType) { var genericDef = type.GetGenericTypeDefinition(); if (genericDef == typeof(IList <>) || genericDef == typeof(IReadOnlyList <>)) { if (typeof(IFlatBufferUnion).IsAssignableFrom(type.GetGenericArguments()[0])) { typeModel = new ListVectorOfUnionTypeModel(type, container); } else { typeModel = new ListVectorTypeModel(type, container); } return(true); } if (genericDef == typeof(Memory <>) || genericDef == typeof(ReadOnlyMemory <>)) { typeModel = new MemoryVectorTypeModel(type, container); return(true); } if (genericDef == typeof(IIndexedVector <,>)) { typeModel = new IndexedVectorTypeModel(type, container); return(true); } } if (typeof(IFlatBufferUnion).IsAssignableFrom(type)) { typeModel = new UnionTypeModel(type, container); return(true); } if (type.IsEnum) { typeModel = new EnumTypeModel(type, container); return(true); } var underlyingType = Nullable.GetUnderlyingType(type); if (underlyingType is not null) { typeModel = new NullableTypeModel(container, type); return(true); } var tableAttribute = type.GetCustomAttribute <FlatBufferTableAttribute>(); var structAttribute = type.GetCustomAttribute <FlatBufferStructAttribute>(); if (tableAttribute is not null && structAttribute is not null) { throw new InvalidFlatBufferDefinitionException($"Type '{CSharpHelpers.GetCompilableTypeName(type)}' is declared as both [FlatBufferTable] and [FlatBufferStruct]."); }
private (string prepareBlock, string serializeBlock) GetUnionSerializeBlocks(int index, TableMemberModel memberModel) { UnionTypeModel unionModel = (UnionTypeModel)memberModel.ItemTypeModel; string valueVariableName = $"index{index}Value"; string discriminatorOffsetVariableName = $"index{index}DiscriminatorOffset"; string valueOffsetVariableName = $"index{index}ValueOffset"; string discriminatorValueVariableName = $"index{index}Discriminator"; string prepareBlock = $@" var {valueVariableName} = item.{memberModel.PropertyInfo.Name}; int {discriminatorOffsetVariableName} = 0; int {valueOffsetVariableName} = 0; byte {discriminatorValueVariableName} = 0; if ({valueVariableName} != null && {valueVariableName}.Discriminator != 0) {{ {discriminatorValueVariableName} = {valueVariableName}.Discriminator; {discriminatorOffsetVariableName} = currentOffset; vtable.{nameof(VTableBuilder.SetOffset)}({index}, currentOffset - tableStart); currentOffset++; currentOffset += {CSharpHelpers.GetFullMethodName(ReflectedMethods.SerializationHelpers_GetAlignmentErrorMethod)}(currentOffset, sizeof(uint)); {valueOffsetVariableName} = currentOffset; vtable.{nameof(VTableBuilder.SetOffset)}({index + 1}, currentOffset - tableStart); currentOffset += sizeof(uint); }}"; List <string> switchCases = new List <string>(); for (int i = 0; i < unionModel.UnionElementTypeModel.Length; ++i) { var elementModel = unionModel.UnionElementTypeModel[i]; var unionIndex = i + 1; string structAdjustment = string.Empty; if (elementModel.SchemaType == FlatBufferSchemaType.Struct) { // Structs are generally written in-line, with the exception of unions. // So, we need to do the normal allocate space dance here, since we're writing // a pointer to a struct. structAdjustment = $@" var writeOffset = context.{nameof(SerializationContext.AllocateSpace)}({elementModel.InlineSize}, {elementModel.Alignment}); writer.{nameof(SpanWriter.WriteUOffset)}(span, {valueOffsetVariableName}, writeOffset, context); {valueOffsetVariableName} = writeOffset;"; } string @case = $@" case {unionIndex}: {{ {structAdjustment} {this.GetSerializeInvocation(elementModel.ClrType, $"{valueVariableName}.Item{unionIndex}", valueOffsetVariableName)} }} break;"; switchCases.Add(@case); } string serializeBlock = $@" if ({discriminatorOffsetVariableName} != 0) {{ {this.GetSerializeInvocation(typeof(byte), discriminatorValueVariableName, discriminatorOffsetVariableName)} switch ({discriminatorValueVariableName}) {{ {string.Join("\r\n", switchCases)} default: throw new InvalidOperationException(""Unexpected""); }} }}"; return(prepareBlock, serializeBlock); }