internal NbtConverter([NotNull] Type contractType) { if (contractType == null) throw new ArgumentNullException("contractType"); this.contractType = contractType; compiledSerializeDelegate = NbtCompiler.GetSerializer(contractType); compiledDeserializeDelegate = NbtCompiler.GetDeserializer(contractType); }
internal NbtConverter([NotNull] Type contractType) { if (contractType == null) { throw new ArgumentNullException("contractType"); } this.contractType = contractType; compiledSerializeDelegate = NbtCompiler.GetSerializer(contractType); compiledDeserializeDelegate = NbtCompiler.GetDeserializer(contractType); }
static NbtSerialize CreateSerializer(Type type) { // This allows our function to call itself, while it is still being built up. NbtSerialize placeholderDelegate = null; // A closure is modified intentionally, at the end of this method: // placeholderDelegate will be replaced with reference to the compiled function. // ReSharper disable once AccessToModifiedClosure Expression<Func<NbtSerialize>> placeholderExpr = () => placeholderDelegate; ParentSerializers.Add(type, placeholderExpr); try { // Define function arguments ParameterExpression argTagName = Expression.Parameter(typeof(string), "tagName"); ParameterExpression argValue = Expression.Parameter(type, "value"); // Create our resolver and emitter var callResolver = new CallResolver(); CodeEmitter codeEmitter = new SerializeCodeEmitter(argTagName, argValue, callResolver); // Define return label LabelTarget returnTarget = Expression.Label(codeEmitter.ReturnValue.Type); // Build up the type-specific list of expressions that perform serialization List<Expression> propSerializersList = MakePropertySerializers(codeEmitter, type); if (callResolver.HasParameters) { propSerializersList.InsertRange(0, callResolver.GetParameterAssignmentList()); } Expression serializersExpr; if (propSerializersList.Count == 0) { serializersExpr = Expression.Empty(); } else if (propSerializersList.Count == 1) { serializersExpr = propSerializersList[0]; } else { serializersExpr = Expression.Block(propSerializersList); } // Create function-wide variables -- includes root tag and serializer delegates var vars = new List<ParameterExpression> { codeEmitter.ReturnValue }; if (callResolver.HasParameters) { vars.AddRange(callResolver.GetParameterList()); } // Construct the method body BlockExpression method = Expression.Block( vars, // if( argValue == null ) Expression.IfThen( Expression.ReferenceEqual(argValue, Expression.Constant(null)), // throw new ArgumentNullException("value"); Expression.Throw(Expression.New(ArgumentNullExceptionCtor, Expression.Constant("value")))), codeEmitter.GetPreamble(), // (run the generated serializing code) serializersExpr, // return varRootTag; Expression.Return(returnTarget, codeEmitter.ReturnValue, codeEmitter.ReturnValue.Type), Expression.Label(returnTarget, Expression.Constant(null, typeof(NbtCompound)))); // compile Expression<NbtSerialize> methodLambda = Expression.Lambda<NbtSerialize>(method, argTagName, argValue); #if DEBUG_NBTSERIALIZE_COMPILER // When in debug mode, print the expression tree to stdout. PropertyInfo propInfo = typeof(Expression) .GetProperty("DebugView", BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Instance); var debugView = (string)propInfo.GetValue(methodLambda, null); Debug.WriteLine(debugView); #endif NbtSerialize compiledMethod = methodLambda.Compile(); // modify the closure created earlier, to allow recursive calls placeholderDelegate = compiledMethod; SerializerCache.Add(type, compiledMethod); return compiledMethod; } finally { ParentSerializers.Remove(type); } }