// valueType maybe a ConstantExpression and then Prune optimizes unreachable branches out static Expression MatchOrElse(Expression valueType, BondDataType expectedType, TypeHandlerCompiletime handler, Expression orElse) { return(PrunedExpression.IfThenElse( Expression.Equal(valueType, Expression.Constant(expectedType)), handler(expectedType), orElse)); }
Expression Value(IParser parser, Expression valueType, RuntimeSchema schema) { Debug.Assert(schema.HasValue); if (parser.IsBonded || (untaggedWriter && schema.IsBonded)) { return(parser.Bonded(value => writer.WriteBonded(PrunedExpression.Convert(value, typeof(IBonded))))); } if (schema.IsStruct) { return(GenerateSerialize(Struct, parser, schema)); } if (schema.IsMap) { return(GenerateSerialize(Map, parser, schema)); } if (schema.IsContainer) { return(GenerateSerialize(Container, parser, schema)); } return(parser.Scalar(valueType, schema.TypeDef.id, value => writer.Write(value, schema.TypeDef.id))); }
Expression FieldValue(IParser parser, Expression var, Expression valueType, Type schemaType, bool initialize) { Expression body; if (schemaType.IsBondStruct() && var.Type.IsValueType()) { // Special handling for properties of struct types: we deserialize into // a temp variable and then assign the value to the property. var temp = Expression.Variable(var.Type, "temp"); body = Expression.Block( new[] { temp }, Value(parser, temp, valueType, schemaType, true), Expression.Assign(var, temp)); } else { body = Value(parser, var, valueType, schemaType, initialize); } if (schemaType.IsBondContainer() || schemaType.IsBondStruct() || schemaType.IsBondNullable()) { var expectedType = Expression.Constant(schemaType.GetBondDataType()); return(PrunedExpression.IfThenElse( Expression.Equal(valueType, expectedType), body, ThrowExpression.InvalidTypeException(expectedType, valueType))); } return(body); }
public static Expression While(Expression whileCondition, Expression body, LabelTarget breakLabel) { return(Expression.Loop( PrunedExpression.IfThenElse( whileCondition, body, Expression.Break(breakLabel)), breakLabel)); }
Expression Value(IParser parser, Expression valueType) { if (parser.IsBonded) { return(parser.Bonded(value => writer.WriteBonded(PrunedExpression.Convert(value, typeof(IBonded))))); } var switchCases = new List <DeferredSwitchCase> { PrunedExpression.SwitchCase( () => GenerateSerialize(Container, parser), BondDataType.BT_LIST, BondDataType.BT_SET), PrunedExpression.SwitchCase( () => GenerateSerialize(Map, parser), BondDataType.BT_MAP), PrunedExpression.SwitchCase( () => GenerateSerialize(Struct, parser), BondDataType.BT_STRUCT) }; switchCases.AddRange( from type in new[] { BondDataType.BT_BOOL, BondDataType.BT_UINT8, BondDataType.BT_UINT16, BondDataType.BT_UINT32, BondDataType.BT_UINT64, BondDataType.BT_FLOAT, BondDataType.BT_DOUBLE, BondDataType.BT_STRING, BondDataType.BT_INT8, BondDataType.BT_INT16, BondDataType.BT_INT32, BondDataType.BT_INT64, BondDataType.BT_WSTRING } select PrunedExpression.SwitchCase( () => parser.Scalar(Expression.Constant(type), type, value => writer.Write(value, type)), type)); return(PrunedExpression.Switch( valueType, ThrowExpression.InvalidTypeException(valueType), switchCases)); }
public Expression Assign(Expression left, Expression right) { var leftType = left.Type; if (leftType != right.Type && leftType.IsGenericType() && leftType.GetGenericTypeDefinition() == typeof(Nullable <>)) { leftType = leftType.GetGenericArguments()[0]; } var value = Convert(right, leftType); return(Expression.Assign(left, PrunedExpression.Convert(value, left.Type))); }
Expression CheckedValue(IParser parser, Expression var, Expression valueType, Type schemaType, bool initialize) { var body = Value(parser, var, valueType, schemaType, initialize); if (schemaType.IsBondContainer() || schemaType.IsBondStruct() || schemaType.IsBondNullable()) { var expectedType = Expression.Constant(schemaType.GetBondDataType()); return(PrunedExpression.IfThenElse( Expression.Equal(valueType, expectedType), body, ThrowExpression.InvalidTypeException(expectedType, valueType))); } return(body); }
Expression Field(ITransform transform, Expression structVar, UInt16 id, ISchemaField schemaField, IField field) { var fieldSchemaType = schemaField.GetSchemaType(); var fieldId = Expression.Constant(id); var fieldType = Expression.Constant(fieldSchemaType.GetBondDataType()); var fieldValue = DataExpression.PropertyOrField(structVar, schemaField.Name); var parser = new ObjectParser(this, fieldValue, fieldSchemaType); var processField = field != null ? field.Value(parser, fieldType) : transform.UnknownField(parser, fieldType, fieldId) ?? Expression.Empty(); var omitField = field != null ? field.Omitted : Expression.Empty(); Expression cannotOmit; if (fieldSchemaType.IsBondStruct() || fieldSchemaType.IsBonded() || schemaField.GetModifier() != Modifier.Optional) { cannotOmit = Expression.Constant(true); } else { var defaultValue = schemaField.GetDefaultValue(); if (fieldSchemaType.IsBondBlob()) { cannotOmit = Expression.NotEqual( typeAlias.Convert(fieldValue, fieldSchemaType), Expression.Default(typeof(ArraySegment <byte>))); } else if (fieldSchemaType.IsBondContainer()) { cannotOmit = defaultValue == null ? Expression.NotEqual(fieldValue, Expression.Constant(null)) : Expression.NotEqual(ContainerCount(fieldValue), Expression.Constant(0)); } else { cannotOmit = Expression.NotEqual(fieldValue, Expression.Constant(defaultValue)); } } return(PrunedExpression.IfThenElse(cannotOmit, processField, omitField)); }
/// <summary> /// Generate expression provided by Serialize delegate, either as inline expression or a lambda call /// </summary> /// <param name="serialize">Delegate to generate serialization expression</param> /// <param name="parser">Parser used for the source of serialization</param> /// <param name="writer">Writer to use for serialization</param> /// <param name="inline">True if the expression can be inlined</param> /// <remarks> /// Generates lambda calls for recursive schemas to avoid infinitely inlining the same expression. /// Expression is considered the same when both serialize delegate and parser are the same. It is /// caller's responsibility to assure that the pair of serialize delegate and parser can be used /// to identify generated expression. For object serializer, when the parser is ObjectParser, this /// is generally guaranteed by using parser instance per schema type. Transcoding may use a single /// parser instance but different Serialize delegates for each transcoded schema type (e.g. when /// the delegate captures schema metadata). /// </remarks> protected Expression GenerateSerialize(Serialize serialize, IParser parser, ParameterExpression writer, bool inline) { var key = new KeyValuePair <IParser, Serialize>(parser, serialize); inline = inline && inProgress.Count != 0 && !inProgress.Contains(key); Expression body; inProgress.Push(key); if (inline) { body = serialize(parser); if (parser.ReaderParam != parser.ReaderValue && parser.ReaderValue.Type.IsBondStruct()) { body = Expression.Block( new[] { parser.ReaderParam }, Expression.Assign(parser.ReaderParam, Expression.Convert(parser.ReaderValue, parser.ReaderParam.Type)), body); } } else { int index; if (!serializeIndex.TryGetValue(key, out index)) { index = serializeActions.Count; serializeIndex[key] = index; serializeActions.Add(null); serializeActions[index] = Expression.Lambda <Action <R, W> >( serialize(parser), parser.ReaderParam, writer); } body = Expression.Invoke( deferredSerialize, PrunedExpression.Convert(parser.ReaderValue, parser.ReaderParam.Type), writer, Expression.Constant(index)); } inProgress.Pop(); return(body); }
Expression Container(IParser parser, RuntimeSchema schema) { var expectedValueType = schema.HasValue ? schema.TypeDef.element.id : (BondDataType?)null; return(parser.Container(expectedValueType, (valueParser, elementType, next, count) => { var body = ControlExpression.While(next, Expression.Block( writer.WriteItemBegin(), schema.HasValue ? Value(valueParser, elementType, schema.GetElementSchema()) : Value(valueParser, elementType), writer.WriteItemEnd())); var blob = parser.Blob(count); if (blob != null) { body = PrunedExpression.IfThenElse( Expression.Equal(elementType, Expression.Constant(BondDataType.BT_INT8)), writer.WriteBytes(blob), body); // For binary protocols we can write blob directly using protocols's WriteBytes // even if the container is not a blob (blob is BT_LIST of BT_INT8). if (binaryWriter) { body = PrunedExpression.IfThenElse( Expression.Equal(elementType, Expression.Constant(BondDataType.BT_UINT8)), writer.WriteBytes(blob), body); } } return Expression.Block( writer.WriteContainerBegin(count, elementType), body, writer.WriteContainerEnd()); })); }
Expression Value(IParser parser, Expression var, Expression valueType, Type schemaType, bool initialize) { if (schemaType.IsBondNullable()) { return(Nullable(parser, var, schemaType.GetValueType(), initialize)); } if (schemaType.IsBonded()) { var convert = bondedConvert.MakeGenericMethod(var.Type.GetValueType()); return(parser.Bonded(value => Expression.Assign(var, Expression.Call(value, convert)))); } if (schemaType.IsBondStruct()) { if (parser.IsBonded) { var deserialize = bondedDeserialize.MakeGenericMethod(schemaType); return(parser.Bonded(value => Expression.Assign(var, Expression.Call(value, deserialize)))); } return(Deserialize(parser, var, var.Type, schemaType, initialize)); } if (schemaType.IsBondMap()) { return(Map(parser, var, schemaType, initialize)); } if (schemaType.IsBondContainer()) { return(Container(parser, var, schemaType, initialize)); } return(parser.Scalar(valueType, schemaType.GetBondDataType(), value => typeAlias.Assign(var, PrunedExpression.Convert(value, schemaType)))); }
public Expression Assign(Expression left, Expression right) { var value = Convert(right, left.Type); return(Expression.Assign(left, PrunedExpression.Convert(value, left.Type))); }
Expression Field(ITransform transform, Expression structVar, UInt16 id, ISchemaField schemaField, IField field) { var fieldSchemaType = schemaField.GetSchemaType(); var fieldId = Expression.Constant(id); var fieldType = Expression.Constant(fieldSchemaType.GetBondDataType()); var fieldValue = DataExpression.PropertyOrField(structVar, schemaField.Name); ObjectParser parser = null; Expression blob = null; ParameterExpression convertedBlob = null; // To avoid calling Convert multiple times on the same aliased Blob // we must construct a new ObjectParser with the expected return type of // of Convert if (fieldSchemaType.IsBondBlob()) { blob = typeAlias.Convert(fieldValue, fieldSchemaType); convertedBlob = Expression.Variable(blob.Type, "convertedBlob"); if (blob.Type != fieldValue.Type) { parser = new ObjectParser(this, convertedBlob, convertedBlob.Type); } } parser = parser ?? new ObjectParser(this, fieldValue, fieldSchemaType); var processField = field != null ? field.Value(parser, fieldType) : transform.UnknownField(parser, fieldType, fieldId) ?? Expression.Empty(); var omitField = field != null ? field.Omitted : Expression.Empty(); Expression cannotOmit; if (fieldSchemaType.IsBondStruct() || fieldSchemaType.IsBonded() || schemaField.GetModifier() != Modifier.Optional) { cannotOmit = Expression.Constant(true); if (fieldSchemaType.IsBondBlob()) { return(Expression.Block( new[] { convertedBlob }, Expression.Assign(convertedBlob, blob), processField)); } } else { var defaultValue = schemaField.GetDefaultValue(); if (fieldSchemaType.IsBondBlob()) { var notEqual = Expression.NotEqual( convertedBlob, Expression.Default(typeof(ArraySegment <byte>))); return(Expression.Block( new[] { convertedBlob }, Expression.Assign(convertedBlob, blob), PrunedExpression.IfThenElse(notEqual, processField, omitField))); } else if (fieldSchemaType.IsBondContainer()) { cannotOmit = defaultValue == null ? Expression.NotEqual(fieldValue, Expression.Constant(null)) : Expression.NotEqual(ContainerCount(fieldValue), Expression.Constant(0)); } else { cannotOmit = Expression.NotEqual(fieldValue, Expression.Constant(defaultValue)); } } return(PrunedExpression.IfThenElse(cannotOmit, processField, omitField)); }