Selaa lähdekoodia

First pass at using Reflection.Emit

Brad Robinson 11 vuotta sitten
vanhempi
commit
42951e70e4

+ 63 - 0
EmitDev/EmitDev.csproj

@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{C70C2324-5076-497A-B93F-C3C49DFDB045}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>EmitDev</RootNamespace>
+    <AssemblyName>EmitDev</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <TargetFrameworkProfile>Client</TargetFrameworkProfile>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\PetaJson.cs">
+      <Link>PetaJson.cs</Link>
+    </Compile>
+    <Compile Include="..\PetaJsonEmit.cs">
+      <Link>PetaJsonEmit.cs</Link>
+    </Compile>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 99 - 0
EmitDev/Program.cs

@@ -0,0 +1,99 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using PetaJson;
+using System.Globalization;
+
+namespace EmitDev
+{
+    [Json]
+    class Stuff
+    {
+        public string Name;
+        public string Address;
+    }
+
+    [Json]
+    class Person : IJsonWriting, IJsonWritten
+    {
+        public string StringField;
+        public int IntField;
+        public double DoubleField;
+        public bool BoolField;
+        public char CharField;
+        public DateTime DateTimeField;
+        public byte[] BlobField;
+        public List<Stuff> StuffsField;
+
+        public string StringProp { get; set; }
+        public int IntProp { get; set; }
+        public double DoubleProp { get; set; }
+        public bool BoolProp { get; set; }
+        public char CharProp { get; set; }
+        public DateTime DateTimeProp { get; set; }
+        public byte[] BlobProp { get; set; }
+        public List<Stuff> StuffsProp { get; set; }
+
+        public int? NullableField1;
+        public int? NullableField2;
+        public int? NullableProp1;
+        public int? NullableProp2;
+
+        void IJsonWritten.OnJsonWritten(IJsonWriter w)
+        {
+            w.WriteRaw("/* OnJsonWritten */ ");
+        }
+
+        void IJsonWriting.OnJsonWriting(IJsonWriter w)
+        {
+            w.WriteRaw("/* OnJsonWriting */");
+        }
+    }
+
+    class Program
+    {
+
+        static void Main(string[] args)
+        {
+            JsonEmit.Init();   
+
+            var p = new Person()
+            {
+                StringField = "Hello World",
+                IntField = 23,
+                DoubleField = 99.99,
+                BoolField = false,
+                CharField = 'X',
+                DateTimeField = DateTime.Now,
+                BlobField = new byte[] { 1, 2, 3, 4},
+                StuffsField = new List<Stuff>() { new Stuff() { Name="Brad", Address="Home" } },
+
+                StringProp = "Hello World",
+                IntProp = 23,
+                DoubleProp = 99.99,
+                BoolProp = false,
+                CharProp = 'X',
+                DateTimeProp = DateTime.Now,
+                BlobProp = new byte[] { 1, 2, 3, 4},
+                StuffsProp = new List<Stuff>() { new Stuff() { Name="Brad", Address="Home" } },
+
+                NullableField1 = null,
+                NullableField2 = 23,
+                NullableProp1 = null,
+                NullableProp2 = 23,
+            };
+
+            var json = Json.Format(p);
+            Console.WriteLine(json);
+
+            var p2 = Json.Parse<Person>(json);
+
+            Console.WriteLine();
+            Console.WriteLine();
+
+            var json2 = Json.Format(p2);
+            Console.WriteLine(json2);
+        }
+    }
+}

+ 36 - 0
EmitDev/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("EmitDev")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("EmitDev")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("43b1567f-dfaf-4b72-9172-43a259ba6c47")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 285 - 292
PetaJson.cs

@@ -18,9 +18,6 @@ using System.Collections;
 #if PETAJSON_DYNAMIC
 using System.Dynamic;
 #endif
-#if PETAJSON_EMIT
-using System.Reflection.Emit;
-#endif
 
 namespace PetaJson
 {
@@ -203,7 +200,7 @@ namespace PetaJson
         // Register a parser for a specified type
         public static void RegisterParser(Type type, Func<IJsonReader, Type, object> parser)
         {
-            Internal.Reader._typeReaders[type] = parser;
+            Internal.Reader._typeParsers[type] = parser;
         }
 
         // Register a typed parser
@@ -224,6 +221,16 @@ namespace PetaJson
             RegisterParser(typeof(T), literal => parser(literal));
         }
 
+        public static void SetTypeFormatterResolver(Func<Type, Action<IJsonWriter, object>> resolver)
+        {
+            Internal.Writer._typeWriterResolver = resolver;
+        }
+
+        public static void SetTypeIntoParserResolver(Func<Type, Action<IJsonReader, object>> resolver)
+        {
+            Internal.Reader._typeIntoParserResolver = resolver;
+        }
+
         // Register a factory for instantiating objects (typically abstract classes)
         // Callback will be invoked for each key in the dictionary until it returns an object
         // instance and which point it will switch to serialization using reflection
@@ -249,7 +256,6 @@ namespace PetaJson
 
             return resolved;
         }
-
     }
 
     // Called before loading via reflection
@@ -283,14 +289,31 @@ namespace PetaJson
         void OnJsonWritten(IJsonWriter w);
     }
 
+    public enum LiteralKind
+    {
+        None,
+        String,
+        Null,
+        True,
+        False,
+        SignedInteger,
+        UnsignedInteger,
+        FloatingPoint,
+    }
+
     // Passed to registered parsers
     public interface IJsonReader
     {
         object ReadLiteral(Func<object, object> converter);
-        object Parse(Type type);
-        T Parse<T>();
         void ReadDictionary(Action<string> callback);
         void ReadArray(Action callback);
+        object Parse(Type type);
+        T Parse<T>();
+        void ParseInto(object into);
+
+        LiteralKind GetLiteralKind();
+        string GetLiteralString();
+        void NextToken();
     }
 
     // Passed to registered formatters
@@ -303,6 +326,7 @@ namespace PetaJson
         void WriteValue(object value);
         void WriteElement();
         void WriteKey(string key);
+        void WriteKeyNoEscaping(string key);
     }
 
     // Exception thrown for any parse error
@@ -311,7 +335,9 @@ namespace PetaJson
         public JsonParseException(Exception inner, JsonLineOffset position) : 
             base(string.Format("Json parse error at {0} - {1}", position, inner.Message), inner)
         {
+            Position = position;
         }
+        public JsonLineOffset Position;
     }
 
     // Stores a line, character offset in source file
@@ -397,32 +423,34 @@ namespace PetaJson
         {
             static Reader()
             {
+                _typeIntoParserResolver = ResolveParseInto;
+
                 Func<IJsonReader, Type, object> simpleConverter = (reader, type) =>
                 {
                     return reader.ReadLiteral(literal => Convert.ChangeType(literal, type, CultureInfo.InvariantCulture));
                 };
 
-                _typeReaders.Add(typeof(string), simpleConverter);
-                _typeReaders.Add(typeof(char), simpleConverter);
-                _typeReaders.Add(typeof(bool), simpleConverter);
-                _typeReaders.Add(typeof(byte), simpleConverter);
-                _typeReaders.Add(typeof(sbyte), simpleConverter);
-                _typeReaders.Add(typeof(short), simpleConverter);
-                _typeReaders.Add(typeof(ushort), simpleConverter);
-                _typeReaders.Add(typeof(int), simpleConverter);
-                _typeReaders.Add(typeof(uint), simpleConverter);
-                _typeReaders.Add(typeof(long), simpleConverter);
-                _typeReaders.Add(typeof(ulong), simpleConverter);
-                _typeReaders.Add(typeof(decimal), simpleConverter);
-                _typeReaders.Add(typeof(float), simpleConverter);
-                _typeReaders.Add(typeof(double), simpleConverter);
-
-                _typeReaders.Add(typeof(DateTime), (reader, type) =>
+                _typeParsers.Add(typeof(string), simpleConverter);
+                _typeParsers.Add(typeof(char), simpleConverter);
+                _typeParsers.Add(typeof(bool), simpleConverter);
+                _typeParsers.Add(typeof(byte), simpleConverter);
+                _typeParsers.Add(typeof(sbyte), simpleConverter);
+                _typeParsers.Add(typeof(short), simpleConverter);
+                _typeParsers.Add(typeof(ushort), simpleConverter);
+                _typeParsers.Add(typeof(int), simpleConverter);
+                _typeParsers.Add(typeof(uint), simpleConverter);
+                _typeParsers.Add(typeof(long), simpleConverter);
+                _typeParsers.Add(typeof(ulong), simpleConverter);
+                _typeParsers.Add(typeof(decimal), simpleConverter);
+                _typeParsers.Add(typeof(float), simpleConverter);
+                _typeParsers.Add(typeof(double), simpleConverter);
+
+                _typeParsers.Add(typeof(DateTime), (reader, type) =>
                 {
                     return reader.ReadLiteral(literal => Utils.FromUnixMilliseconds((long)Convert.ChangeType(literal, typeof(long), CultureInfo.InvariantCulture)));
                 });
 
-                _typeReaders.Add(typeof(byte[]), (reader, type) =>
+                _typeParsers.Add(typeof(byte[]), (reader, type) =>
                 {
                     return reader.ReadLiteral(literal => Convert.FromBase64String((string)Convert.ChangeType(literal, typeof(string), CultureInfo.InvariantCulture)));
                 });
@@ -434,6 +462,17 @@ namespace PetaJson
                 _options = options;
             }
 
+            static Action<IJsonReader, object> ResolveParseInto(Type type)
+            {
+                var ri = ReflectionInfo.GetReflectionInfo(type);
+                if (ri != null)
+                    return ri.ParseInto;
+                else
+                    return null;
+            }
+
+            
+
             Tokenizer _tokenizer;
             JsonOptions _options;
 
@@ -449,7 +488,7 @@ namespace PetaJson
             public object ReadLiteral(Func<object, object> converter)
             {
                 _tokenizer.Check(Token.Literal);
-                var retv = converter(_tokenizer.Literal);
+                var retv = converter(_tokenizer.LiteralValue);
                 _tokenizer.NextToken();
                 return retv;
             }
@@ -462,7 +501,7 @@ namespace PetaJson
             public object Parse(Type type)
             {
                 // Null?
-                if (_tokenizer.CurrentToken == Token.Literal && _tokenizer.Literal == null)
+                if (_tokenizer.CurrentToken == Token.Literal && _tokenizer.LiteralKind == LiteralKind.Null)
                 {
                     _tokenizer.NextToken();
                     return null;
@@ -475,17 +514,11 @@ namespace PetaJson
 
                 // See if we have a reader
                 Func<IJsonReader, Type, object> typeReader;
-                if (Reader._typeReaders.TryGetValue(type, out typeReader))
+                if (Reader._typeParsers.TryGetValue(type, out typeReader))
                 {
                     return typeReader(this, type);
                 }
 
-                // Enumerated type?
-                if (type.IsEnum)
-                {
-                    return ReadLiteral(literal => Enum.Parse(type, (string)literal));
-                }
-                
                 // See if we have factory
                 Func<IJsonReader, string, object> typeFactory;
                 if (Reader._typeFactories.TryGetValue(type, out typeFactory))
@@ -530,14 +563,21 @@ namespace PetaJson
                     return into;
                 }
 
-                // Is it a type we can parse into?
-                if (CanParseInto(type))
+                // Do we already have an into reader?
+                Action<IJsonReader, object> typeIntoReader;
+                if (Reader._typeIntoParsers.TryGetValue(type, out typeIntoReader))
                 {
                     var into = Activator.CreateInstance(type);
                     ParseInto(into);
                     return into;
                 }
 
+                // Enumerated type?
+                if (type.IsEnum)
+                {
+                    return ReadLiteral(literal => Enum.Parse(type, (string)literal));
+                }
+
                 // Array?
                 if (type.IsArray && type.GetArrayRank() == 1)
                 {
@@ -577,47 +617,22 @@ namespace PetaJson
                 }
 
                 // Untyped literal?
-                if (_tokenizer.CurrentToken == Token.Literal && type.IsAssignableFrom(_tokenizer.Literal.GetType()))
+                if (_tokenizer.CurrentToken == Token.Literal && type.IsAssignableFrom(_tokenizer.LiteralType))
                 {
-                    var lit = _tokenizer.Literal;
+                    var lit = _tokenizer.LiteralValue;
                     _tokenizer.NextToken();
                     return lit;
                 }
 
-                throw new InvalidDataException(string.Format("syntax error - unexpected token {0}", _tokenizer.CurrentToken));
-            }
-
-            public static bool CanParseInto(Type type)
-            {
-                /* These two checks are redundant as they're covered by IDictionary/IList below
-                if (typeof(IDictionary<,>).IsAssignableFrom(type))
-                    return true;
-
-                if (typeof(IList<>).IsAssignableFrom(type))
-                    return true;
-                */
-
-                if (type.IsArray)
-                    return false;
-
-                if (typeof(IDictionary).IsAssignableFrom(type))
-                    return true;
-
-                if (typeof(IList).IsAssignableFrom(type))
-                    return true;
-
-                if (ReflectionInfo.GetReflectionInfo(type) != null)
-                    return true;
-
-                return false;
-            }
-
-            public void ParseInto(object into)
-            {
-                if (TryParseInto(into))
-                    return;
+                // Is it a type we can parse into?
+                if ((type.IsClass || (type.IsValueType && !type.IsPrimitive)) && type != typeof(object))
+                {
+                    var into = Activator.CreateInstance(type);
+                    ParseInto(into);
+                    return into;
+                }
 
-                throw new InvalidOperationException(string.Format("Don't know how to load into '{0}'", into.GetType().FullName));
+                throw new InvalidDataException(string.Format("syntax error - unexpected token {0}", _tokenizer.CurrentToken));
             }
 
             public Type FindGenericInterface(Type type, Type tItf)
@@ -632,13 +647,21 @@ namespace PetaJson
                 return null;
             }
 
-            public bool TryParseInto(object into)
+            public void ParseInto(object into)
             {
                 if (into == null)
-                    return false;
+                    return;
 
                 var type = into.GetType();
 
+                // Existing parse into handler?
+                Action<IJsonReader,object> parseInto;
+                if (_typeIntoParsers.TryGetValue(type, out parseInto))
+                {
+                    parseInto(this, into);
+                    return;
+                }
+
                 // Generic dictionary?
                 var dictType = FindGenericInterface(type, typeof(IDictionary<,>));
                 if (dictType!=null)
@@ -655,7 +678,7 @@ namespace PetaJson
                         dict.Add(Convert.ChangeType(key, typeKey), Parse(typeValue));
                     });
 
-                    return true;
+                    return;
                 }
 
                 // Generic list
@@ -673,7 +696,7 @@ namespace PetaJson
                         list.Add(Parse(typeElement));
                     });
 
-                    return true;
+                    return;
                 }
 
                 // Untyped dictionary
@@ -685,7 +708,7 @@ namespace PetaJson
                     {
                         objDict[key] = Parse(typeof(Object));
                     });
-                    return true;
+                    return;
                 }
 
                 // Untyped list
@@ -697,18 +720,19 @@ namespace PetaJson
                     {
                         objList.Add(Parse(typeof(Object)));
                     });
-                    return true;
+                    return;
                 }
 
                 // Use reflection?
-                var ri = ReflectionInfo.GetReflectionInfo(type);
-                if (ri != null)
+                var tw = _typeIntoParserResolver(type);
+                if (tw != null)
                 {
-                    ri.ParseInto(this, into);
-                    return true;
+                    _typeIntoParsers[type] = tw;
+                    tw(this, into);
+                    return;
                 }
 
-                return false;
+                throw new InvalidOperationException(string.Format("Don't know how to parse into type '{0}'", type.FullName));
             }
 
             public T Parse<T>()
@@ -716,6 +740,21 @@ namespace PetaJson
                 return (T)Parse(typeof(T));
             }
 
+            public LiteralKind GetLiteralKind() 
+            { 
+                return _tokenizer.LiteralKind; 
+            }
+            
+            public string GetLiteralString() 
+            { 
+                return _tokenizer.String; 
+            }
+
+            public void NextToken() 
+            { 
+                _tokenizer.NextToken(); 
+            }
+
             public void ReadDictionary(Action<string> callback)
             {
                 _tokenizer.Skip(Token.OpenBrace);
@@ -735,9 +774,9 @@ namespace PetaJson
                     {
                         key = _tokenizer.String;
                     }
-                    else if (_tokenizer.CurrentToken == Token.Literal && _tokenizer.Literal is String)
+                    else if (_tokenizer.CurrentToken == Token.Literal && _tokenizer.LiteralKind == LiteralKind.String)
                     {
-                        key = (string)_tokenizer.Literal;
+                        key = (string)_tokenizer.LiteralValue;
                     }
                     else
                     {
@@ -747,12 +786,12 @@ namespace PetaJson
                     _tokenizer.NextToken();
                     _tokenizer.Skip(Token.Colon);
 
-                    _tokenizer.DidMove = false;
+                    var pos = _tokenizer.CurrentTokenPosition;
 
                     if (!callback(key))
                         return;
 
-                    if (!_tokenizer.DidMove)
+                    if (pos.Line==_tokenizer.CurrentTokenPosition.Line && pos.Offset == _tokenizer.CurrentTokenPosition.Offset)
                     {
                         // The callback didn't handle the key, so skip the value
                         Parse(typeof(object));
@@ -792,7 +831,9 @@ namespace PetaJson
                 _tokenizer.Skip(Token.CloseSquare);
             }
 
-            public static Dictionary<Type, Func<IJsonReader, Type, object>> _typeReaders = new Dictionary<Type, Func<IJsonReader, Type, object>>();
+            public static Dictionary<Type, Func<IJsonReader, Type, object>> _typeParsers = new Dictionary<Type, Func<IJsonReader, Type, object>>();
+            public static Dictionary<Type, Action<IJsonReader, object>> _typeIntoParsers = new Dictionary<Type, Action<IJsonReader, object>>();
+            public static Func<Type, Action<IJsonReader, object>> _typeIntoParserResolver;
             public static Dictionary<Type, Func<IJsonReader, string, object>> _typeFactories = new Dictionary<Type, Func<IJsonReader, string, object>>();
         }
 
@@ -800,6 +841,8 @@ namespace PetaJson
         {
             static Writer()
             {
+                _typeWriterResolver = ResolveTypeWriter;
+
                 // Strings
                 _typeWriters.Add(typeof(string), (w, o) => w.WriteStringLiteral((string)o));
                 _typeWriters.Add(typeof(char), (w, o) => w.WriteStringLiteral(((char)o).ToString()));
@@ -836,6 +879,16 @@ namespace PetaJson
             }
 
             public static Dictionary<Type, Action<IJsonWriter, object>> _typeWriters = new Dictionary<Type, Action<IJsonWriter, object>>();
+            public static Func<Type, Action<IJsonWriter, object>> _typeWriterResolver;
+
+            static Action<IJsonWriter, object> ResolveTypeWriter(Type type)
+            {
+                var ri = ReflectionInfo.GetReflectionInfo(type);
+                if (ri != null)
+                    return ri.Write;
+                else
+                    return null;
+            }
 
             public Writer(TextWriter w, JsonOptions options)
             {
@@ -1072,10 +1125,11 @@ namespace PetaJson
                 }
 
                 // Try using reflection
-                var ri = ReflectionInfo.GetReflectionInfo(type);
-                if (ri != null)
+                var tw = _typeWriterResolver(type);
+                if (tw != null)
                 {
-                    ri.Write(this, value);
+                    _typeWriters[type] = tw;
+                    tw(this, value);
                     return;
                 }
 
@@ -1084,8 +1138,11 @@ namespace PetaJson
             }
         }
 
-        class JsonMemberInfo
+        public class JsonMemberInfo
         {
+            public string JsonKey;
+            public bool KeepInstance;
+
             MemberInfo _mi;
             public MemberInfo Member
             {
@@ -1106,9 +1163,6 @@ namespace PetaJson
                 }
             }
 
-            public string JsonKey;
-            public bool KeepInstance;
-
             public Type MemberType
             {
                 get
@@ -1127,103 +1181,19 @@ namespace PetaJson
             public Action<object, object> SetValue;
             public Func<object, object> GetValue;
 
-
-#if PETAJSON_EMIT
-            static MethodInfo fnChangeType = typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(Object), typeof(Type) });
-            static MethodInfo fnGetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) });
-
-            public static Action<object, object> CreateSetter(PropertyInfo pi)
-            {
-                var m = new DynamicMethod("dynamic_property_setter_" + pi.DeclaringType.Name + "_" + pi.Name, null, new Type[] { typeof(object), typeof(object) }, true);
-                var il = m.GetILGenerator();
-
-                il.Emit(OpCodes.Ldarg_0);
-                il.Emit(pi.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, pi.DeclaringType);
-
-                il.Emit(OpCodes.Ldarg_1);
-                il.Emit(OpCodes.Ldtoken, pi.PropertyType);
-                il.Emit(OpCodes.Call, fnGetTypeFromHandle);
-                il.Emit(OpCodes.Call, fnChangeType);
-                il.Emit(pi.PropertyType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, pi.PropertyType);
-
-                il.Emit(OpCodes.Callvirt, pi.GetSetMethod());
-                il.Emit(OpCodes.Ret);
-
-                return (Action<object, object>)m.CreateDelegate(typeof(Action<object, object>));
-            }
-
-            public static Action<object, object> CreateSetter(FieldInfo fi)
-            {
-                var m = new DynamicMethod("dynamic_field_setter_" + fi.DeclaringType.Name + "_" + fi.Name, null, new Type[] { typeof(object), typeof(object) }, true);
-                var il = m.GetILGenerator();
-
-
-                var fnChangeType = typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(Object), typeof(Type) });
-                var fnGetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) });
-
-                il.Emit(OpCodes.Ldarg_0);
-                il.Emit(fi.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, fi.DeclaringType);
-
-                il.Emit(OpCodes.Ldarg_1);
-                il.Emit(OpCodes.Ldtoken, fi.FieldType);
-                il.Emit(OpCodes.Call, fnGetTypeFromHandle);
-                il.Emit(OpCodes.Call, fnChangeType);
-                il.Emit(fi.FieldType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, fi.FieldType);
-
-                il.Emit(OpCodes.Stfld, fi);
-                il.Emit(OpCodes.Ret);
-
-                return (Action<object, object>)m.CreateDelegate(typeof(Action<object, object>));
-            }
-
-            public static Func<object, object> CreateGetter(PropertyInfo pi)
-            {
-                var m = new DynamicMethod("dynamic_property_setter_" + pi.DeclaringType.Name + "_" + pi.Name, typeof(object), new Type[] { typeof(object) }, true);
-                var il = m.GetILGenerator();
-
-                il.Emit(OpCodes.Ldarg_0);
-                il.Emit(pi.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, pi.DeclaringType);
-
-                il.Emit(OpCodes.Callvirt, pi.GetGetMethod());
-
-                if (pi.PropertyType.IsValueType)
-                    il.Emit(OpCodes.Box, pi.PropertyType);
-
-                il.Emit(OpCodes.Ret);
-
-                return (Func<object, object>)m.CreateDelegate(typeof(Func<object, object>));
-            }
-            public static Func<object, object> CreateGetter(FieldInfo fi)
-            {
-                var m = new DynamicMethod("dynamic_field_setter_" + fi.DeclaringType.Name + "_" + fi.Name, typeof(object), new Type[] { typeof(object) }, true);
-                var il = m.GetILGenerator();
-
-                il.Emit(OpCodes.Ldarg_0);
-                il.Emit(fi.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, fi.DeclaringType);
-
-                il.Emit(OpCodes.Ldfld, fi);
-                if (fi.FieldType.IsValueType)
-                    il.Emit(OpCodes.Box, fi.FieldType);
-
-                il.Emit(OpCodes.Ret);
-
-                return (Func<object, object>)m.CreateDelegate(typeof(Func<object, object>));
-            }
-#else
-        public static Action<object, object> CreateSetter(PropertyInfo pi) { return (obj, val) => pi.SetValue(obj, val, null); }
-        public static Action<object, object> CreateSetter(FieldInfo fi) { return fi.SetValue; }
-        public static Func<object, object> CreateGetter(PropertyInfo pi) { return (obj) => pi.GetValue(obj, null); }
-        public static Func<object, object> CreateGetter(FieldInfo fi) { return fi.GetValue; }
-#endif
+            public static Action<object, object> CreateSetter(PropertyInfo pi) { return (obj, val) => pi.SetValue(obj, val, null); }
+            public static Action<object, object> CreateSetter(FieldInfo fi) { return fi.SetValue; }
+            public static Func<object, object> CreateGetter(PropertyInfo pi) { return (obj) => pi.GetValue(obj, null); }
+            public static Func<object, object> CreateGetter(FieldInfo fi) { return fi.GetValue; }
         }
 
-        class ReflectionInfo
+        public class ReflectionInfo
         {
-            List<JsonMemberInfo> _members;
+            public List<JsonMemberInfo> Members;
 
             static Dictionary<Type, ReflectionInfo> _cache = new Dictionary<Type, ReflectionInfo>();
 
-            public void Write(Writer w, object val)
+            public void Write(IJsonWriter w, object val)
             {
                 w.WriteDictionary(() =>
                 {
@@ -1231,7 +1201,7 @@ namespace PetaJson
                     if (writing != null)
                         writing.OnJsonWriting(w);
 
-                    foreach (var jmi in _members)
+                    foreach (var jmi in Members)
                     {
                         w.WriteKeyNoEscaping(jmi.JsonKey);
                         w.WriteValue(jmi.GetValue(val));
@@ -1243,6 +1213,27 @@ namespace PetaJson
                 });
             }
 
+            public void ParseInto(IJsonReader r, object into)
+            {
+                var loading = into as IJsonLoading;
+
+                r.ReadDictionary(key =>
+                {
+
+                    if (loading != null)
+                    {
+                        loading.OnJsonLoading(r);
+                        loading = null;
+                    }
+
+                    ParseFieldOrProperty(r, into, key);
+                });
+
+                var loaded = into as IJsonLoaded;
+                if (loaded != null)
+                    loaded.OnJsonLoaded(r);
+            }
+
             // The member info is stored in a list (as opposed to a dictionary) so that
             // the json is written in the same order as the fields/properties are defined
             // On loading, we assume the fields will be in the same order, but need to
@@ -1252,10 +1243,10 @@ namespace PetaJson
             int _lastFoundIndex = 0;
             bool FindMemberInfo(string name, out JsonMemberInfo found)
             {
-                for (int i = 0; i < _members.Count; i++)
+                for (int i = 0; i < Members.Count; i++)
                 {
-                    int index = (i + _lastFoundIndex) % _members.Count;
-                    var jmi = _members[index];
+                    int index = (i + _lastFoundIndex) % Members.Count;
+                    var jmi = Members[index];
                     if (jmi.JsonKey == name)
                     {
                         _lastFoundIndex = index;
@@ -1267,7 +1258,7 @@ namespace PetaJson
                 return false;
             }
 
-            public void ParseFieldOrProperty(Reader r, object into, string key)
+            public void ParseFieldOrProperty(IJsonReader r, object into, string key)
             {
                 var lf = into as IJsonLoadField;
                 if (lf != null)
@@ -1279,7 +1270,7 @@ namespace PetaJson
                 JsonMemberInfo jmi;
                 if (FindMemberInfo(key, out jmi))
                 {
-                    if (jmi.KeepInstance && Reader.CanParseInto(jmi.MemberType))
+                    if (jmi.KeepInstance)
                     {
                         var subInto = jmi.GetValue(into);
                         if (subInto != null)
@@ -1295,26 +1286,6 @@ namespace PetaJson
                 }
             }
 
-            public void ParseInto(Reader r, object into)
-            {
-                var loading = into as IJsonLoading;
-
-                r.ReadDictionary(key => {
-
-                    if (loading != null)
-                    {
-                        loading.OnJsonLoading(r);
-                        loading = null;
-                    }
-                    
-                    ParseFieldOrProperty(r, into, key);
-                });
-
-                var loaded = into as IJsonLoaded;
-                if (loaded != null)
-                    loaded.OnJsonLoaded(r);
-            }
-
             public static ReflectionInfo GetReflectionInfo(Type type)
             {
                 // Already created?
@@ -1398,6 +1369,12 @@ namespace PetaJson
                 // Work out properties and fields
                 var members = GetAllFieldsAndProperties(type).Select(x => callback(x)).Where(x => x != null).ToList();
 
+                var invalid = members.FirstOrDefault(x => x.KeepInstance && x.MemberType.IsValueType);
+                if (invalid!=null)
+                {
+                    throw new InvalidOperationException(string.Format("KeepInstance=true can only be applied to reference types ({0}.{1})", type.FullName, invalid.Member));
+                }
+
                 // Must have some members
                 if (!members.Any())
                     return null;
@@ -1405,7 +1382,7 @@ namespace PetaJson
                 // Create reflection info
                 var ri = new ReflectionInfo()
                 {
-                    _members = members,
+                    Members = members,
                 };
 
                 return ri;
@@ -1530,10 +1507,6 @@ namespace PetaJson
                 _reader = new RewindableTextReader(r);
                 _options = options;
 
-                _nextCharPos.Line = 0;
-                _nextCharPos.Offset = 0;
-                _currentCharPos = _nextCharPos;
-
                 // Load up
                 NextChar();
                 NextToken();
@@ -1543,14 +1516,54 @@ namespace PetaJson
             JsonOptions _options;
             StringBuilder _sb = new StringBuilder();
 
-            JsonLineOffset _nextCharPos;
             JsonLineOffset _currentCharPos;
             JsonLineOffset CurrentTokenPos;
             char _currentChar;
             char _pendingChar;
             public Token CurrentToken;
+            public LiteralKind LiteralKind;
             public string String;
-            public object Literal;
+
+            public object LiteralValue
+            {
+                get
+                {
+                    if (CurrentToken != Token.Literal)
+                        throw new InvalidOperationException("token is not a literal");
+                    switch (LiteralKind)
+                    {
+                        case LiteralKind.Null: return null;
+                        case LiteralKind.False: return false;
+                        case LiteralKind.True: return true;
+                        case LiteralKind.String: return String;
+                        case LiteralKind.SignedInteger: return long.Parse(String, CultureInfo.InvariantCulture);
+                        case LiteralKind.UnsignedInteger: return ulong.Parse(String, CultureInfo.InvariantCulture);
+                        case LiteralKind.FloatingPoint: return double.Parse(String, CultureInfo.InvariantCulture);
+                    }
+                    return null;
+                }
+            }
+
+            public Type LiteralType
+            {
+                get
+                {
+                    if (CurrentToken != Token.Literal)
+                        throw new InvalidOperationException("token is not a literal");
+                    switch (LiteralKind)
+                    {
+                        case LiteralKind.Null: return typeof(Object);
+                        case LiteralKind.False: return typeof(Boolean);
+                        case LiteralKind.True: return typeof(Boolean);
+                        case LiteralKind.String: return typeof(string);
+                        case LiteralKind.SignedInteger: return typeof(long);
+                        case LiteralKind.UnsignedInteger: return typeof(ulong);
+                        case LiteralKind.FloatingPoint: return typeof(double);
+                    }
+
+                    return null;
+                }
+            }
 
             // this object represents the entire state of the reader
             // which when combined with the RewindableTextReader allows
@@ -1559,36 +1572,33 @@ namespace PetaJson
             {
                 public ReaderState(Tokenizer tokenizer)
                 {
-                    _nextCharPos = tokenizer._nextCharPos;
                     _currentCharPos = tokenizer._currentCharPos;
                     CurrentTokenPos = tokenizer.CurrentTokenPos;
                     _currentChar = tokenizer._currentChar;
                     _pendingChar = tokenizer._pendingChar;
                     CurrentToken = tokenizer.CurrentToken;
                     _string = tokenizer.String;
-                    _literal = tokenizer.Literal;
+                    _literalKind = tokenizer.LiteralKind;
                 }
 
                 public void Apply(Tokenizer tokenizer)
                 {
-                    tokenizer._nextCharPos = _nextCharPos;
                     tokenizer._currentCharPos = _currentCharPos;
                     tokenizer.CurrentTokenPos = CurrentTokenPos;
                     tokenizer._currentChar = _currentChar;
                     tokenizer._pendingChar = _pendingChar;
                     tokenizer.CurrentToken = CurrentToken;
                     tokenizer.String = _string;
-                    tokenizer.Literal = _literal;
+                    tokenizer.LiteralKind = _literalKind;
                 }
 
-                public JsonLineOffset _nextCharPos;
                 public JsonLineOffset _currentCharPos;
                 public JsonLineOffset CurrentTokenPos;
                 public char _currentChar;
                 public char _pendingChar;
                 public Token CurrentToken;
+                public LiteralKind _literalKind;
                 public string _string;
-                public object _literal;
             }
 
             Stack<ReaderState> _bookmarks = new Stack<ReaderState>();
@@ -1613,58 +1623,44 @@ namespace PetaJson
 
             char NextChar()
             {
-                // Normalize line endings to '\n'
-                char ch;
-                if (_pendingChar != '\0')
-                {
-                    ch = _pendingChar;
-                }
-                else
-                {
-                    ch = _reader.ReadChar();
-                    if (ch == '\r')
-                    {
-                        ch = _reader.ReadChar();
-                        if (ch != '\n')
-                        {
-                            _pendingChar = ch;
-                            ch = '\n';
-                        }
-                    }
-                }
-
-                _currentCharPos = _nextCharPos;
-
-                // Update line position counter
-                if (ch == '\n')
-                {
-                    _nextCharPos.Line++;
-                    _nextCharPos.Offset = 0;
-                }
-                else
-                {
-                    _nextCharPos.Offset++;
-                }
-
-                return _currentChar = ch;
-            }
-
-            public bool DidMove
-            {
-                get;
-                set;
+                _currentCharPos.Offset++;
+                return _currentChar = _reader.ReadChar();
             }
 
             public void NextToken()
             {
-                DidMove = true;
-
-
                 while (true)
                 {
-                    while (_currentChar == '\t' || _currentChar == ' ' || _currentChar == '\r' || _currentChar == '\n')
+                    while (true)
                     {
-                        NextChar();
+                        if (_currentChar == '\r')
+                        {
+                            if (NextChar() == '\n')
+                            {
+                                NextChar();
+                            }
+                            _currentCharPos.Line++;
+                            _currentCharPos.Offset = 0;
+                        }
+                        else if (_currentChar == '\n')
+                        {
+                            if (NextChar() == '\r')
+                            {
+                                NextChar();
+                            }
+                            _currentCharPos.Line++;
+                            _currentCharPos.Offset = 0;
+                        }
+                        else if (_currentChar == ' ')
+                        {
+                            NextChar();
+                        }
+                        else if (_currentChar == '\t')
+                        {
+                            NextChar();
+                        }
+                        else
+                            break;
                     }
 
                     CurrentTokenPos = _currentCharPos;
@@ -1682,17 +1678,17 @@ namespace PetaJson
                         switch (String)
                         {
                             case "true":
-                                Literal = true;
+                                LiteralKind = LiteralKind.True;
                                 CurrentToken =  Token.Literal;
                                 break;
 
                             case "false":
-                                Literal = false;
+                                LiteralKind = LiteralKind.False;
                                 CurrentToken =  Token.Literal;
                                 break;
 
                             case "null":
-                                Literal = null;
+                                LiteralKind = LiteralKind.Null;
                                 CurrentToken =  Token.Literal;
                                 break;
 
@@ -1792,8 +1788,9 @@ namespace PetaJson
                                 }
                                 else if (_currentChar == quoteKind)
                                 {
-                                    Literal = _sb.ToString();
-                                    CurrentToken =  Token.Literal;
+                                    String = _sb.ToString();
+                                    CurrentToken = Token.Literal;
+                                    LiteralKind = LiteralKind.String;
                                     NextChar();
                                     return;
                                 }
@@ -1874,26 +1871,27 @@ namespace PetaJson
                     throw new InvalidDataException(string.Format("syntax error - invalid character following number '{0}'", _currentChar));
 
 
-                // Convert type
-                try
+                String = _sb.ToString();
+                CurrentToken = Token.Literal;
+                if (fp)
                 {
-                    if (fp)
-                    {
-                        Literal = double.Parse(_sb.ToString(), System.Globalization.CultureInfo.InvariantCulture);
-                    }
-                    else if (signed)
-                    {
-                        Literal = long.Parse(_sb.ToString(), System.Globalization.CultureInfo.InvariantCulture);
-                    }
-                    else
-                    {
-                        Literal = ulong.Parse(_sb.ToString(), System.Globalization.CultureInfo.InvariantCulture);
-                    }
+                    LiteralKind = LiteralKind.FloatingPoint;
+                }
+                else if (signed)
+                {
+                    LiteralKind = LiteralKind.SignedInteger;
+                }
+                else
+                {
+                    LiteralKind = LiteralKind.UnsignedInteger;
+                }
+                /*
                 }
                 catch
                 {
                     throw new InvalidDataException(string.Format("syntax error - incorrectly formatted number '{0}'", _sb.ToString()));
                 }
+                 */
 
             }
 
@@ -1937,11 +1935,6 @@ namespace PetaJson
             {
                 return Char.IsLetter(ch) || ch == '_' || ch == '$';
             }
-
-            public static bool IsIdentifier(string str)
-            {
-                return IsIdentifierLeadChar(str[0]) && str.All(x => IsIdentifierChar(x));
-            }
         }
     }
 }

+ 6 - 0
PetaJson.sln

@@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 11.00
 # Visual Studio 2010
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestCases", "TestCases\TestCases.csproj", "{B1C908D5-CFEE-41D7-8D5C-50D0DD697E2C}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmitDev", "EmitDev\EmitDev.csproj", "{C70C2324-5076-497A-B93F-C3C49DFDB045}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x86 = Debug|x86
@@ -13,6 +15,10 @@ Global
 		{B1C908D5-CFEE-41D7-8D5C-50D0DD697E2C}.Debug|x86.Build.0 = Debug|x86
 		{B1C908D5-CFEE-41D7-8D5C-50D0DD697E2C}.Release|x86.ActiveCfg = Release|x86
 		{B1C908D5-CFEE-41D7-8D5C-50D0DD697E2C}.Release|x86.Build.0 = Release|x86
+		{C70C2324-5076-497A-B93F-C3C49DFDB045}.Debug|x86.ActiveCfg = Debug|x86
+		{C70C2324-5076-497A-B93F-C3C49DFDB045}.Debug|x86.Build.0 = Debug|x86
+		{C70C2324-5076-497A-B93F-C3C49DFDB045}.Release|x86.ActiveCfg = Release|x86
+		{C70C2324-5076-497A-B93F-C3C49DFDB045}.Release|x86.Build.0 = Release|x86
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 549 - 0
PetaJsonEmit.cs

@@ -0,0 +1,549 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Reflection.Emit;
+using System.Reflection;
+using System.Globalization;
+using System.IO;
+
+namespace PetaJson
+{
+    public static class JsonEmit
+    {
+        public static void Init()
+        {
+            Json.SetTypeFormatterResolver(Internal.Emit.MakeFormatter);
+            Json.SetTypeIntoParserResolver(Internal.Emit.MakeParser);
+        }
+    }
+
+    namespace Internal
+    {
+        static class Emit
+        {
+            public static Action<IJsonWriter, object> MakeFormatter(Type type)
+            {
+                // Get the reflection info for this type
+                var ri = ReflectionInfo.GetReflectionInfo(type);
+                if (ri == null)
+                    return null;
+
+                // Create a dynamic method that can do the work
+                var method = new DynamicMethod("dynamic_formatter", null, new Type[] { typeof(IJsonWriter), typeof(object) }, true);
+                var il = method.GetILGenerator();
+
+                // Cast/unbox the target object and store in local variable
+                var locTypedObj = il.DeclareLocal(type);
+                il.Emit(OpCodes.Ldarg_1);
+                il.Emit(type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type);
+                il.Emit(OpCodes.Stloc, locTypedObj);
+
+                // Get Invariant CultureInfo
+                var locInvariant = il.DeclareLocal(typeof(IFormatProvider));
+                il.Emit(OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod());
+                il.Emit(OpCodes.Stloc, locInvariant);
+
+                // These are the types we'll call .ToString(Culture.InvariantCulture) on
+                var toStringTypes = new Type[] { 
+                    typeof(int), typeof(uint), typeof(long), typeof(ulong), 
+                    typeof(short), typeof(ushort), typeof(decimal), 
+                    typeof(byte), typeof(sbyte)
+                };
+
+                // Theses types we also generate for
+                var otherSupportedTypes = new Type[] {
+                    typeof(double), typeof(float), typeof(string), typeof(char), typeof(bool)
+                };
+
+                // Call IJsonWriting if implemented
+                if (typeof(IJsonWriting).IsAssignableFrom(type))
+                {
+                    il.Emit(OpCodes.Ldloc, locTypedObj);
+                    if (type.IsValueType)
+                        il.Emit(OpCodes.Box, type);
+                    il.Emit(OpCodes.Castclass, typeof(IJsonWriting));
+                    il.Emit(OpCodes.Ldarg_0);
+                    il.Emit(OpCodes.Callvirt, typeof(IJsonWriting).GetMethod("OnJsonWriting", new Type[] { typeof(IJsonWriter) }));
+                }
+
+                // Process all members
+                foreach (var m in ri.Members)
+                {
+                    // Ignore write only properties
+                    var pi = m.Member as PropertyInfo;
+                    if (pi!=null && pi.GetGetMethod() == null)
+                    {
+                        continue;
+                    }
+
+                    // Write the Json key
+                    il.Emit(OpCodes.Ldarg_0);
+                    il.Emit(OpCodes.Ldstr, m.JsonKey);
+                    il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteKeyNoEscaping", new Type[] { typeof(string) }));
+
+                    // Load the writer
+                    il.Emit(OpCodes.Ldarg_0);
+
+                    // Get the member type
+                    var memberType = m.MemberType;
+
+                    // Load the target object
+                    if (type.IsValueType)
+                    {
+                        il.Emit(OpCodes.Ldloca, locTypedObj);
+                    }
+                    else
+                    {
+                        il.Emit(OpCodes.Ldloc, locTypedObj);
+                    }
+
+                    // Work out if we need to the value or it's address on the stack
+                    bool NeedValueAddress = (memberType.IsValueType && (toStringTypes.Contains(memberType) || otherSupportedTypes.Contains(memberType)));
+                    if (Nullable.GetUnderlyingType(memberType) != null)
+                    {
+                        NeedValueAddress = true;
+                    }
+
+                    // Property?
+                    if (pi != null)
+                    {
+                        // Call property's get method
+                        if (type.IsValueType)
+                            il.Emit(OpCodes.Call, pi.GetGetMethod());
+                        else
+                            il.Emit(OpCodes.Callvirt, pi.GetGetMethod());
+
+                        // If we need the address then store in a local and take it's address
+                        if (NeedValueAddress)
+                        {
+                            var locTemp = il.DeclareLocal(memberType);
+                            il.Emit(OpCodes.Stloc, locTemp);
+                            il.Emit(OpCodes.Ldloca, locTemp);
+                        }
+                    }
+
+                    // Field?
+                    var fi = m.Member as FieldInfo;
+                    if (fi != null)
+                    {
+                        if (NeedValueAddress)
+                        {
+                            il.Emit(OpCodes.Ldflda, fi);
+                        }
+                        else
+                        {
+                            il.Emit(OpCodes.Ldfld, fi);
+                        }
+                    }
+
+                    Label? lblFinished = null;
+
+                    // Is it a nullable type?
+                    var typeUnderlying = Nullable.GetUnderlyingType(memberType);
+                    if (typeUnderlying != null)
+                    {
+                        // Duplicate the address so we can call get_HasValue() and then get_Value()
+                        il.Emit(OpCodes.Dup);
+
+                        // Define some labels
+                        var lblHasValue = il.DefineLabel();
+                        lblFinished = il.DefineLabel();
+
+                        // Call has_Value
+                        il.Emit(OpCodes.Call, memberType.GetProperty("HasValue").GetGetMethod());
+                        il.Emit(OpCodes.Brtrue, lblHasValue);
+
+                        // No value, write "null:
+                        il.Emit(OpCodes.Pop);
+                        il.Emit(OpCodes.Ldstr, "null");
+                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteRaw", new Type[] { typeof(string) }));
+                        il.Emit(OpCodes.Br_S, lblFinished.Value);
+
+                        // Get it's value
+                        il.MarkLabel(lblHasValue);
+                        il.Emit(OpCodes.Call, memberType.GetProperty("Value").GetGetMethod());
+
+                        // Switch to the underlying type from here on
+                        memberType = typeUnderlying;
+                        NeedValueAddress = (memberType.IsValueType && (toStringTypes.Contains(memberType) || otherSupportedTypes.Contains(memberType)));
+
+                        // Work out again if we need the address of the value
+                        if (NeedValueAddress)
+                        {
+                            var locTemp = il.DeclareLocal(memberType);
+                            il.Emit(OpCodes.Stloc, locTemp);
+                            il.Emit(OpCodes.Ldloca, locTemp);
+                        }
+                    }
+
+                    // ToString()
+                    if (toStringTypes.Contains(memberType))
+                    {
+                        // Convert to string
+                        il.Emit(OpCodes.Ldloc, locInvariant);
+                        il.Emit(OpCodes.Call, memberType.GetMethod("ToString", new Type[] { typeof(IFormatProvider) }));
+                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteRaw", new Type[] { typeof(string) }));
+                    }
+
+                    // ToString("R")
+                    else if (memberType == typeof(float) || memberType == typeof(double))
+                    {
+                        il.Emit(OpCodes.Ldstr, "R");
+                        il.Emit(OpCodes.Ldloc, locInvariant);
+                        il.Emit(OpCodes.Call, memberType.GetMethod("ToString", new Type[] { typeof(string), typeof(IFormatProvider) }));
+                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteRaw", new Type[] { typeof(string) }));
+                    }
+
+                    // String?
+                    else if (memberType == typeof(string))
+                    {
+                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteStringLiteral", new Type[] { typeof(string) }));
+                    }
+
+                    // Char?
+                    else if (memberType == typeof(char))
+                    {
+                        il.Emit(OpCodes.Call, memberType.GetMethod("ToString", new Type[] {  }));
+                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteStringLiteral", new Type[] { typeof(string) }));
+                    }
+
+                    // Bool?
+                    else if (memberType == typeof(bool))
+                    {
+                        var lblTrue = il.DefineLabel();
+                        var lblCont = il.DefineLabel();
+                        il.Emit(OpCodes.Brtrue_S, lblTrue);
+                        il.Emit(OpCodes.Ldstr, "false");
+                        il.Emit(OpCodes.Br_S, lblCont);
+                        il.MarkLabel(lblTrue);
+                        il.Emit(OpCodes.Ldstr, "true");
+                        il.MarkLabel(lblCont);
+                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteRaw", new Type[] { typeof(string) }));
+                    }
+
+                    // NB: We don't support DateTime as it's format can be changed
+
+                    else
+                    {
+                        // Unsupported type, pass through
+                        if (memberType.IsValueType)
+                        {
+                            il.Emit(OpCodes.Box, memberType);
+                        }
+                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteValue", new Type[] { typeof(object) }));
+                    }
+
+                    if (lblFinished.HasValue)
+                        il.MarkLabel(lblFinished.Value);
+                }
+
+                // Call IJsonWritten
+                if (typeof(IJsonWritten).IsAssignableFrom(type))
+                {
+                    il.Emit(OpCodes.Ldloc, locTypedObj);
+                    if (type.IsValueType)
+                        il.Emit(OpCodes.Box, type);
+                    il.Emit(OpCodes.Castclass, typeof(IJsonWritten));
+                    il.Emit(OpCodes.Ldarg_0);
+                    il.Emit(OpCodes.Callvirt, typeof(IJsonWritten).GetMethod("OnJsonWritten", new Type[] { typeof(IJsonWriter) }));
+                }
+
+                // Done!
+                il.Emit(OpCodes.Ret);
+
+                // Create delegate to our IL code
+                var impl = (Action<IJsonWriter, object>)method.CreateDelegate(typeof(Action<IJsonWriter, object>));
+
+                // Wrap it in a call to WriteDictionary
+                return (w, obj) =>
+                {
+                    w.WriteDictionary(() =>
+                    {
+                        impl(w, obj);
+                    });
+                };
+
+            }
+
+            public static Action<IJsonReader, object> MakeParser(Type type)
+            {
+                if (type.IsValueType)
+                    throw new NotImplementedException("Value types aren't supported through reflection");
+
+                // Get the reflection info for this type
+                var ri = ReflectionInfo.GetReflectionInfo(type);
+                if (ri == null)
+                    return null;
+
+                var setters = new Dictionary<string, Action<IJsonReader, object>>();
+
+                // These types we'll call <type>.Parse(reader.String) on
+                var numericTypes = new Type[] { 
+                    typeof(int), typeof(uint), typeof(long), typeof(ulong), 
+                    typeof(short), typeof(ushort), typeof(decimal), 
+                    typeof(byte), typeof(sbyte), 
+                    typeof(double), typeof(float)
+                };
+
+                try
+                {
+                    // Process all members
+                    foreach (var m in ri.Members)
+                    {
+                        // Ignore write only properties
+                        var pi = m.Member as PropertyInfo;
+                        if (pi != null && pi.GetSetMethod() == null)
+                        {
+                            continue;
+                        }
+
+                        // Create a dynamic method that can do the work
+                        var method = new DynamicMethod("dynamic_parser", null, new Type[] { typeof(IJsonReader), typeof(object) }, true);
+                        var il = method.GetILGenerator();
+
+                        if (m.KeepInstance)
+                        {
+                            throw new NotImplementedException("Emit KeepInstance not implemented");
+                        }
+
+                        // Load the target
+                        il.Emit(OpCodes.Ldarg_1);
+                        il.Emit(OpCodes.Castclass, type);
+
+                        if (m.MemberType == typeof(string))
+                        {
+                            // check we have a string
+                            il.Emit(OpCodes.Ldarg_0);
+                            il.Emit(OpCodes.Call, typeof(Emit).GetMethod("GetLiteralString", new Type[] { typeof(IJsonReader) }));
+
+                            il.Emit(OpCodes.Ldarg_0);
+                            il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("NextToken", new Type[] { }));
+                        }
+
+                        else if (m.MemberType == typeof(bool))
+                        {
+                            il.Emit(OpCodes.Ldarg_0);
+                            il.Emit(OpCodes.Call, typeof(Emit).GetMethod("GetLiteralBool", new Type[] { typeof(IJsonReader) }));
+
+                            il.Emit(OpCodes.Ldarg_0);
+                            il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("NextToken", new Type[] { }));
+                        }
+
+                        else if (m.MemberType == typeof(char))
+                        {
+                            il.Emit(OpCodes.Ldarg_0);
+                            il.Emit(OpCodes.Call, typeof(Emit).GetMethod("GetLiteralChar", new Type[] { typeof(IJsonReader) }));
+                            il.Emit(OpCodes.Ldarg_0);
+                            il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("NextToken", new Type[] { }));
+                        }
+
+                        else if (numericTypes.Contains(m.MemberType))
+                        {
+                            il.Emit(OpCodes.Ldarg_0);
+                            il.Emit(OpCodes.Call, typeof(Emit).GetMethod("GetLiteralNumber", new Type[] { typeof(IJsonReader) }));
+
+                            // Convert to a string
+                            il.Emit(OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod());
+                            il.Emit(OpCodes.Call, m.MemberType.GetMethod("Parse", new Type[] { typeof(string), typeof(IFormatProvider) }));
+
+                            il.Emit(OpCodes.Ldarg_0);
+                            il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("NextToken", new Type[] { }));
+                        }
+
+                        else
+                        {
+                            il.Emit(OpCodes.Ldarg_0);
+                            il.Emit(OpCodes.Ldtoken, m.MemberType);
+                            il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }));
+                            il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("Parse", new Type[] { typeof(Type) }));
+                            il.Emit(m.MemberType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, m.MemberType);
+                        }
+
+                        if (pi != null)
+                        {
+                            il.Emit(OpCodes.Callvirt, pi.GetSetMethod());
+                        }
+
+                        var fi = m.Member as FieldInfo;
+                        if (fi != null)
+                        {
+                            il.Emit(OpCodes.Stfld, fi);
+                        }
+
+                        // Cast/unbox the target object and store in local variable
+                        /*
+                        var locTypedObj = il.DeclareLocal(type);
+                        il.Emit(OpCodes.Ldarg_1);
+                        il.Emit(type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type);
+                        il.Emit(OpCodes.Stloc, locTypedObj);
+
+                        // Get Invariant CultureInfo
+                        var locInvariant = il.DeclareLocal(typeof(IFormatProvider));
+                        il.Emit(OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod());
+                        il.Emit(OpCodes.Stloc, locInvariant);
+                         */
+
+                        il.Emit(OpCodes.Ret);
+
+                        // Store the handler in map
+                        setters.Add(m.JsonKey, (Action<IJsonReader, object>)method.CreateDelegate(typeof(Action<IJsonReader, object>)));
+                    }
+                }
+                catch (Exception x)
+                {
+                    int y = 3;
+                }
+
+
+                return (reader, obj) =>
+                {
+                    reader.ReadDictionary(key =>
+                    {
+                        var lf = obj as IJsonLoadField;
+                        if (lf != null)
+                        {
+                            if (lf.OnJsonField(reader, key))
+                                return;
+                        }
+
+                        Action<IJsonReader, object> setter;
+                        if (setters.TryGetValue(key, out setter))
+                        {
+                            setter(reader, obj);
+                        }
+                    });
+                };
+            }
+
+            public static bool GetLiteralBool(IJsonReader r)
+            {
+                switch (r.GetLiteralKind())
+                {
+                    case LiteralKind.True:
+                        return true;
+
+                    case LiteralKind.False:
+                        return false;
+
+                    default:
+                        throw new InvalidDataException("expected a boolean value");
+                }
+            }
+
+            public static char GetLiteralChar(IJsonReader r)
+            {
+                if (r.GetLiteralKind() != LiteralKind.String)
+                    throw new InvalidDataException("expected a single character string literal");
+                var str = r.GetLiteralString();
+                if (str==null || str.Length!=1)
+                    throw new InvalidDataException("expected a single character string literal");
+
+                return str[0];
+            }
+
+            public static string GetLiteralString(IJsonReader r)
+            {
+                if (r.GetLiteralKind() != LiteralKind.String)
+                    throw new InvalidDataException("expected a string literal");
+                return r.GetLiteralString();
+            }
+
+            public static string GetLiteralNumber(IJsonReader r)
+            {
+                switch (r.GetLiteralKind())
+                {
+                    case LiteralKind.SignedInteger:
+                    case LiteralKind.UnsignedInteger:
+                    case LiteralKind.FloatingPoint:
+                        return r.GetLiteralString();
+                }
+                throw new InvalidDataException("expected a numeric literal");
+            }
+
+        }
+    }
+
+    /*
+            static MethodInfo fnChangeType = typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(Object), typeof(Type) });
+            static MethodInfo fnGetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) });
+
+            public static Action<object, object> CreateSetter(PropertyInfo pi)
+            {
+                var m = new DynamicMethod("dynamic_property_setter_" + pi.DeclaringType.Name + "_" + pi.Name, null, new Type[] { typeof(object), typeof(object) }, true);
+                var il = m.GetILGenerator();
+
+                il.Emit(OpCodes.Ldarg_0);
+                il.Emit(pi.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, pi.DeclaringType);
+
+                il.Emit(OpCodes.Ldarg_1);
+                il.Emit(OpCodes.Ldtoken, pi.PropertyType);
+                il.Emit(OpCodes.Call, fnGetTypeFromHandle);
+                il.Emit(OpCodes.Call, fnChangeType);
+                il.Emit(pi.PropertyType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, pi.PropertyType);
+
+                il.Emit(OpCodes.Callvirt, pi.GetSetMethod());
+                il.Emit(OpCodes.Ret);
+
+                return (Action<object, object>)m.CreateDelegate(typeof(Action<object, object>));
+            }
+
+            public static Action<object, object> CreateSetter(FieldInfo fi)
+            {
+                var m = new DynamicMethod("dynamic_field_setter_" + fi.DeclaringType.Name + "_" + fi.Name, null, new Type[] { typeof(object), typeof(object) }, true);
+                var il = m.GetILGenerator();
+
+
+                var fnChangeType = typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(Object), typeof(Type) });
+                var fnGetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) });
+
+                il.Emit(OpCodes.Ldarg_0);
+                il.Emit(fi.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, fi.DeclaringType);
+
+                il.Emit(OpCodes.Ldarg_1);
+                il.Emit(OpCodes.Ldtoken, fi.FieldType);
+                il.Emit(OpCodes.Call, fnGetTypeFromHandle);
+                il.Emit(OpCodes.Call, fnChangeType);
+                il.Emit(fi.FieldType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, fi.FieldType);
+
+                il.Emit(OpCodes.Stfld, fi);
+                il.Emit(OpCodes.Ret);
+
+                return (Action<object, object>)m.CreateDelegate(typeof(Action<object, object>));
+            }
+
+            public static Func<object, object> CreateGetter(PropertyInfo pi)
+            {
+                var m = new DynamicMethod("dynamic_property_setter_" + pi.DeclaringType.Name + "_" + pi.Name, typeof(object), new Type[] { typeof(object) }, true);
+                var il = m.GetILGenerator();
+
+                il.Emit(OpCodes.Ldarg_0);
+                il.Emit(pi.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, pi.DeclaringType);
+
+                il.Emit(OpCodes.Callvirt, pi.GetGetMethod());
+
+                if (pi.PropertyType.IsValueType)
+                    il.Emit(OpCodes.Box, pi.PropertyType);
+
+                il.Emit(OpCodes.Ret);
+
+                return (Func<object, object>)m.CreateDelegate(typeof(Func<object, object>));
+            }
+            public static Func<object, object> CreateGetter(FieldInfo fi)
+            {
+                var m = new DynamicMethod("dynamic_field_setter_" + fi.DeclaringType.Name + "_" + fi.Name, typeof(object), new Type[] { typeof(object) }, true);
+                var il = m.GetILGenerator();
+
+                il.Emit(OpCodes.Ldarg_0);
+                il.Emit(fi.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, fi.DeclaringType);
+
+                il.Emit(OpCodes.Ldfld, fi);
+                if (fi.FieldType.IsValueType)
+                    il.Emit(OpCodes.Box, fi.FieldType);
+
+                il.Emit(OpCodes.Ret);
+
+                return (Func<object, object>)m.CreateDelegate(typeof(Func<object, object>));
+            }
+     */
+}

+ 8 - 0
TestCases/Program.cs

@@ -7,8 +7,16 @@ namespace TestCases
 {
     class Program
     {
+        int field { get; set; }
+
         static void Main(string[] args)
         {
+            var p = new Program();
+            p.field = 23;
+
+            var str = p.field.ToString();
+
+            PetaJson.JsonEmit.Init();
             PetaTest.Runner.RunMain(args);
         }
     }

+ 3 - 0
TestCases/TestCases.csproj

@@ -46,6 +46,9 @@
     <Compile Include="..\PetaJson.cs">
       <Link>PetaJson.cs</Link>
     </Compile>
+    <Compile Include="..\PetaJsonEmit.cs">
+      <Link>PetaJsonEmit.cs</Link>
+    </Compile>
     <Compile Include="PetaTest.cs" />
     <Compile Include="Program.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

+ 15 - 0
TestCases/TestsGeneral.cs

@@ -187,5 +187,20 @@ namespace TestCases
 		{
 			Assert.Throws<JsonParseException>(() => Json.Parse<object>("\"\\q\""));
         }
+
+        [Test]
+        public void ErrorLocation()
+        {
+            var strJson="{\r\n \r\n \n\r \r \n \t   \"key:\": zzz";
+            try
+            {
+                Json.Parse<object>(strJson);
+            }
+            catch (JsonParseException x)
+            {
+                Assert.AreEqual(x.Position.Line, 5);
+                Assert.AreEqual(x.Position.Offset, 13);
+            }
+        }
     }
 }