├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── MessagePack.FSharpExtensions.sln ├── README.md ├── RELEASE_NOTES.md ├── src └── MessagePack.FSharpExtensions │ ├── DiscriminatedUnionResolver.cs │ ├── FSharpResolver.cs │ ├── Formatters │ ├── FSharpAsyncFormatter.cs │ ├── FSharpCollectionFormatters.cs │ ├── FSharpOptionFormatter.cs │ └── UnitFormatter.cs │ ├── Internal │ ├── DynamicAssembly.cs │ ├── ILGeneratorExtensions.cs │ └── ReflectionExtensions.cs │ ├── MessagePack.FSharpExtensions.csproj │ └── MonoProtection.cs └── tests └── MessagePack.FSharpExtensions.Tests ├── AsyncTest.fs ├── ClassTest.fs ├── CollectionTest.fs ├── DUTest.fs ├── Helper.fs ├── MessagePack.FSharpExtensions.Tests.fsproj ├── OptionTest.fs ├── RecordTest.fs ├── StructDUTest.fs └── UnitTest.fs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | jobs: 5 | build_and_test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - uses: actions/setup-dotnet@v1 10 | with: 11 | dotnet-version: '6.0.406' 12 | - name: build and test 13 | run: | 14 | dotnet restore 15 | dotnet test 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### VisualStudio ### 4 | ## Ignore Visual Studio temporary files, build results, and 5 | ## files generated by popular Visual Studio add-ons. 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Roslyn cache directories 26 | *.ide/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | #NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | sql/ 155 | *.Cache 156 | ClientBin/ 157 | [Ss]tyle[Cc]op.* 158 | ~$* 159 | *~ 160 | *.dbmdl 161 | *.dbproj.schemaview 162 | *.pfx 163 | *.publishsettings 164 | node_modules/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | packages/ 190 | localpackages/ 191 | paket-files 192 | *.orig 193 | .paket/paket.exe 194 | .fake/ 195 | docs/content/release-notes.md 196 | temp/ 197 | .nuget/ 198 | .vs/ 199 | 200 | .idea/ 201 | .ionide/ 202 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017- pocketberserker 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /MessagePack.FSharpExtensions.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.26124.0 4 | MinimumVisualStudioVersion = 15.0.26124.0 5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{420FA7D9-5689-4C91-80A6-702AFADF08AC}" 6 | EndProject 7 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.FSharpExtensions", "src\MessagePack.FSharpExtensions\MessagePack.FSharpExtensions.csproj", "{17A1F405-138E-439F-A8E1-70F20E44A36F}" 8 | EndProject 9 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D6B163A5-CCBD-44B3-A97F-151A25241DF6}" 10 | EndProject 11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.FSharpExtensions.Tests", "tests\MessagePack.FSharpExtensions.Tests\MessagePack.FSharpExtensions.Tests.fsproj", "{4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Debug|x64 = Debug|x64 17 | Debug|x86 = Debug|x86 18 | Release|Any CPU = Release|Any CPU 19 | Release|x64 = Release|x64 20 | Release|x86 = Release|x86 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {17A1F405-138E-439F-A8E1-70F20E44A36F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {17A1F405-138E-439F-A8E1-70F20E44A36F}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {17A1F405-138E-439F-A8E1-70F20E44A36F}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {17A1F405-138E-439F-A8E1-70F20E44A36F}.Debug|x64.Build.0 = Debug|Any CPU 27 | {17A1F405-138E-439F-A8E1-70F20E44A36F}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {17A1F405-138E-439F-A8E1-70F20E44A36F}.Debug|x86.Build.0 = Debug|Any CPU 29 | {17A1F405-138E-439F-A8E1-70F20E44A36F}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {17A1F405-138E-439F-A8E1-70F20E44A36F}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {17A1F405-138E-439F-A8E1-70F20E44A36F}.Release|x64.ActiveCfg = Release|Any CPU 32 | {17A1F405-138E-439F-A8E1-70F20E44A36F}.Release|x64.Build.0 = Release|Any CPU 33 | {17A1F405-138E-439F-A8E1-70F20E44A36F}.Release|x86.ActiveCfg = Release|Any CPU 34 | {17A1F405-138E-439F-A8E1-70F20E44A36F}.Release|x86.Build.0 = Release|Any CPU 35 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}.Debug|x64.ActiveCfg = Debug|Any CPU 38 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}.Debug|x64.Build.0 = Debug|Any CPU 39 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}.Debug|x86.ActiveCfg = Debug|Any CPU 40 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}.Debug|x86.Build.0 = Debug|Any CPU 41 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}.Release|x64.ActiveCfg = Release|Any CPU 44 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}.Release|x64.Build.0 = Release|Any CPU 45 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}.Release|x86.ActiveCfg = Release|Any CPU 46 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC}.Release|x86.Build.0 = Release|Any CPU 47 | EndGlobalSection 48 | GlobalSection(SolutionProperties) = preSolution 49 | HideSolutionNode = FALSE 50 | EndGlobalSection 51 | GlobalSection(NestedProjects) = preSolution 52 | {17A1F405-138E-439F-A8E1-70F20E44A36F} = {420FA7D9-5689-4C91-80A6-702AFADF08AC} 53 | {4FE39CF9-CEC0-4ABE-9965-96662D89EFEC} = {D6B163A5-CCBD-44B3-A97F-151A25241DF6} 54 | EndGlobalSection 55 | GlobalSection(ExtensibilityGlobals) = postSolution 56 | SolutionGuid = {DD9405F0-C348-4D21-B03B-60353C583452} 57 | EndGlobalSection 58 | EndGlobal 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MessagePack.FSharpExtensions 2 | [![NuGet Status](http://img.shields.io/nuget/v/MessagePack.FSharpExtensions.svg?style=flat)](https://www.nuget.org/packages/MessagePack.FSharpExtensions/) 3 | 4 | MessagePack.FSharpExtensions is a [MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp) extension library for F#. 5 | 6 | ## Usage 7 | 8 | ```fsharp 9 | open System 10 | open System.Buffers 11 | open MessagePack 12 | open MessagePack.Resolvers 13 | open MessagePack.FSharp 14 | 15 | [] 16 | type UnionSample = 17 | | Foo of XYZ : int 18 | | Bar of OPQ : string list 19 | 20 | let convertAsMemory<'T> options (value: 'T) = 21 | let memory = ReadOnlyMemory(MessagePackSerializer.Serialize(value, options)) 22 | MessagePackSerializer.Deserialize<'T>(memory, options) 23 | 24 | let convertAsSequence<'T> options (value: 'T) = 25 | let sequence = ReadOnlySequence(MessagePackSerializer.Serialize(value, options)) 26 | MessagePackSerializer.Deserialize<'T>(& sequence, options) 27 | 28 | let dump = function 29 | | Foo x -> 30 | printfn "%d" x 31 | | Bar xs -> 32 | printfn "%A" xs 33 | 34 | let resolver = 35 | Resolvers.CompositeResolver.Create( 36 | FSharpResolver.Instance, 37 | StandardResolver.Instance 38 | ) 39 | 40 | let options = MessagePackSerializerOptions.Standard.WithResolver(resolver) 41 | 42 | Foo 999 43 | |> convertAsMemory options 44 | |> dump 45 | 46 | Bar ["example"] 47 | |> convertAsSequence options 48 | |> dump 49 | ``` 50 | 51 | ## Supported types 52 | 53 | - option 54 | - voption 55 | - list 56 | - map 57 | - set 58 | - Discriminated Union 59 | - Struct Discriminated Union 60 | 61 | Records, Struct Records and Anonymous Records are serialized and deserialized using `DynamicObjectResolver` in `MessagePack-CSharp`. 62 | -------------------------------------------------------------------------------- /RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | ### 4.0.0 - February 19 2023 2 | * drop net50 3 | * update FSharp.Core(to 7.0.200) 4 | * update MessagePack(to 2.4.59) 5 | 6 | ### 3.0.0 - September 26 2021 7 | * support net50(use FSharp.Core 5.0.x) 8 | * support netcoreapp3.1 9 | * update MessagePack(to 2.3.x) 10 | 11 | ### 2.1.0 - May 21 2020 12 | * add voption formatter 13 | 14 | ### 2.0.0 - May 21 2020 15 | * initial support MessagePack 2.x 16 | * update FSharp.Core 4.7.1 17 | * drop .NET Framework and netstandard1.6 18 | * support netstandard2.0 and netcoreapp2.1 19 | 20 | ### 1.4.1 - January 9 2019 21 | * Fix signalr interop #2 22 | * re-support netstandard1.6 23 | 24 | ### 1.4.0 - October 10 2017 25 | * support netstandard2.0(use FSharp.Core 4.2.x) 26 | * drop netstandard1.6 27 | 28 | ### 1.3.0 - October 7 2017 29 | * improve performance 30 | * update dependencies(FSharp.Core 4.1.18, MessagePack 1.6.2) 31 | * fix struct union deserialization bug 32 | 33 | ### 1.2.0 - July 5 2017 34 | * update dependencies(MessagePack 1.4.1) 35 | 36 | ### 1.1.0 - May 20 2017 37 | * Reduce class name length restrictions 38 | * update dependencies(FSharp.Core 4.1.17, MessagePack 1.2.2) 39 | 40 | ### 1.0.0 - March 30 2017 41 | * update dependencies(FSharp.Core 4.1.2, MessagePack 1.1.1) 42 | * disable implicit framework references 43 | 44 | ### 0.1.0 - March 17 2017 45 | * initial release 46 | -------------------------------------------------------------------------------- /src/MessagePack.FSharpExtensions/DiscriminatedUnionResolver.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Yoshifumi Kawai and contributors 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | using System; 22 | using System.Buffers; 23 | using System.Linq; 24 | using System.Reflection; 25 | using System.Reflection.Emit; 26 | using System.Collections.Generic; 27 | using System.Text.RegularExpressions; 28 | using System.Threading; 29 | using MessagePack.Formatters; 30 | using MessagePack.Internal; 31 | using MessagePack.FSharp.Internal; 32 | using Microsoft.FSharp.Reflection; 33 | 34 | namespace MessagePack.FSharp 35 | { 36 | public sealed class DynamicUnionResolver : IFormatterResolver 37 | { 38 | public static readonly DynamicUnionResolver Instance; 39 | 40 | const string ModuleName = "MessagePack.FSharp.DynamicUnionResolver"; 41 | 42 | static readonly Lazy DynamicAssembly; 43 | 44 | static readonly Regex SubtractFullNameRegex = new Regex(@", Version=\d+.\d+.\d+.\d+, Culture=\w+, PublicKeyToken=\w+", RegexOptions.Compiled); 45 | 46 | static int nameSequence = 0; 47 | 48 | private DynamicUnionResolver() { } 49 | 50 | static DynamicUnionResolver() 51 | { 52 | Instance = new DynamicUnionResolver(); 53 | DynamicAssembly = new Lazy(() => new DynamicAssembly(ModuleName)); 54 | } 55 | 56 | public IMessagePackFormatter GetFormatter() 57 | { 58 | return FormatterCache.Formatter; 59 | } 60 | 61 | private static class FormatterCache 62 | { 63 | public static readonly IMessagePackFormatter Formatter; 64 | 65 | static FormatterCache() 66 | { 67 | if (!FSharpType.IsUnion(typeof(T), null)) 68 | { 69 | return; 70 | } 71 | 72 | var ti = typeof(T).GetTypeInfo(); 73 | 74 | var formatterTypeInfo = BuildType(typeof(T)); 75 | if (formatterTypeInfo == null) 76 | { 77 | return; 78 | } 79 | 80 | Formatter = (IMessagePackFormatter)Activator.CreateInstance(formatterTypeInfo.AsType()); 81 | } 82 | } 83 | 84 | static TypeInfo BuildType(Type type) 85 | { 86 | var ti = type.GetTypeInfo(); 87 | // order by key(important for use jump-table of switch) 88 | var unionCases = FSharpType.GetUnionCases(type, null).OrderBy(x => x.Tag).ToArray(); 89 | 90 | var formatterType = typeof(IMessagePackFormatter<>).MakeGenericType(type); 91 | using (MonoProtection.EnterRefEmitLock()) 92 | { 93 | TypeBuilder typeBuilder = DynamicAssembly.Value.DefineType("MessagePack.FSharp.Formatters." + SubtractFullNameRegex.Replace(type.FullName, string.Empty).Replace(".", "_") + "Formatter" + +Interlocked.Increment(ref nameSequence), TypeAttributes.Public | TypeAttributes.Sealed, null, new[] { formatterType }); 94 | 95 | var stringByteKeysFields = new FieldBuilder[unionCases.Length]; 96 | 97 | // create map dictionary 98 | { 99 | var method = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); 100 | 101 | foreach(var unionCase in unionCases) 102 | { 103 | stringByteKeysFields[unionCase.Tag] = typeBuilder.DefineField("stringByteKeysField" + unionCase.Tag, typeof(byte[][]), FieldAttributes.Private | FieldAttributes.InitOnly); 104 | } 105 | 106 | var il = method.GetILGenerator(); 107 | BuildConstructor(type, unionCases, method, stringByteKeysFields, il); 108 | } 109 | 110 | { 111 | var method = typeBuilder.DefineMethod( 112 | "Serialize", 113 | MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, 114 | returnType: null, 115 | parameterTypes: new Type[] { typeof(MessagePackWriter).MakeByRefType(), type, typeof(MessagePackSerializerOptions) }); 116 | method.DefineParameter(1, ParameterAttributes.None, "writer"); 117 | method.DefineParameter(2, ParameterAttributes.None, "value"); 118 | method.DefineParameter(3, ParameterAttributes.None, "options"); 119 | 120 | var il = method.GetILGenerator(); 121 | BuildSerialize(type, unionCases, method, stringByteKeysFields, il, 1); 122 | } 123 | { 124 | MethodBuilder method = typeBuilder.DefineMethod( 125 | "Deserialize", 126 | MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, 127 | type, 128 | new Type[] { refMessagePackReader, typeof(MessagePackSerializerOptions) }); 129 | method.DefineParameter(1, ParameterAttributes.None, "reader"); 130 | method.DefineParameter(2, ParameterAttributes.None, "options"); 131 | 132 | var il = method.GetILGenerator(); 133 | BuildDeserialize( 134 | type, 135 | unionCases, 136 | method, 137 | stringByteKeysFields, 138 | il, 139 | 1); // firstArgIndex:0 is this. 140 | } 141 | 142 | return typeBuilder.CreateTypeInfo(); 143 | } 144 | } 145 | 146 | static void BuildConstructor(Type type, UnionCaseInfo[] infos, ConstructorInfo method, FieldBuilder[] stringByteKeysFields, ILGenerator il) 147 | { 148 | il.EmitLdarg(0); 149 | il.Emit(OpCodes.Call, objectCtor); 150 | 151 | { 152 | foreach (var info in infos) 153 | { 154 | var fields = info.GetFields(); 155 | il.EmitLdarg(0); 156 | il.EmitLdc_I4(fields.Length); 157 | il.Emit(OpCodes.Newarr, typeof(byte[])); 158 | 159 | var index = 0; 160 | foreach (var field in fields) 161 | { 162 | il.Emit(OpCodes.Dup); 163 | il.EmitLdc_I4(index); 164 | il.Emit(OpCodes.Ldstr, field.Name); 165 | il.EmitCall(CodeGenHelpersTypeInfo.GetEncodedStringBytes); 166 | il.Emit(OpCodes.Stelem_Ref); 167 | index++; 168 | } 169 | 170 | il.Emit(OpCodes.Stfld, stringByteKeysFields[info.Tag]); 171 | } 172 | } 173 | 174 | il.Emit(OpCodes.Ret); 175 | } 176 | 177 | 178 | // void Serialize(ref [arg:1]MessagePackWriter writer, [arg:2]T value, [arg:3]MessagePackSerializerOptions options); 179 | static void BuildSerialize( 180 | Type type, 181 | UnionCaseInfo[] infos, 182 | MethodBuilder method, 183 | FieldBuilder[] stringByteKeysFields, 184 | ILGenerator il, 185 | int firstArgIndex 186 | ) 187 | { 188 | var tag = getTag(type); 189 | var ti = type.GetTypeInfo(); 190 | 191 | var argWriter = new ArgumentField(il, firstArgIndex); 192 | var argValue = new ArgumentField(il, firstArgIndex + 1, type); 193 | var argOptions = new ArgumentField(il, firstArgIndex + 2); 194 | 195 | // if(value == null) return WriteNil 196 | if (ti.IsClass) 197 | { 198 | Label elseBody = il.DefineLabel(); 199 | 200 | argValue.EmitLoad(); 201 | il.Emit(OpCodes.Brtrue_S, elseBody); 202 | argWriter.EmitLoad(); 203 | il.EmitCall(MessagePackWriterTypeInfo.WriteNil); 204 | il.Emit(OpCodes.Ret); 205 | 206 | il.MarkLabel(elseBody); 207 | } 208 | 209 | // IMessagePackSerializationCallbackReceiver.OnBeforeSerialize() 210 | if (ti.ImplementedInterfaces.Any(x => x == typeof(IMessagePackSerializationCallbackReceiver))) 211 | { 212 | // call directly 213 | MethodInfo[] runtimeMethods = type.GetRuntimeMethods().Where(x => x.Name == "OnBeforeSerialize").ToArray(); 214 | if (runtimeMethods.Length == 1) 215 | { 216 | argValue.EmitLoad(); 217 | il.Emit(OpCodes.Call, runtimeMethods[0]); // don't use EmitCall helper(must use 'Call') 218 | } 219 | else 220 | { 221 | argValue.EmitLdarg(); // force ldarg 222 | il.EmitBoxOrDoNothing(type); 223 | il.EmitCall(onBeforeSerialize); 224 | } 225 | } 226 | 227 | // IFormatterResolver resolver = options.Resolver; 228 | LocalBuilder localResolver = il.DeclareLocal(typeof(IFormatterResolver)); 229 | argOptions.EmitLoad(); 230 | il.EmitCall(getResolverFromOptions); 231 | il.EmitStloc(localResolver); 232 | 233 | // writer.WriteArrayHeader(2, false); 234 | argWriter.EmitLdarg(); 235 | il.EmitLdc_I4(2); 236 | il.EmitCall(MessagePackWriterTypeInfo.WriteArrayHeader); 237 | 238 | // writer.Write(value.Tag) 239 | argWriter.EmitLdarg(); 240 | if (ti.IsClass) 241 | { 242 | argValue.EmitLdarg(); 243 | } 244 | else 245 | { 246 | argValue.EmitLdarga(); 247 | } 248 | il.EmitCall(tag); 249 | il.EmitCall(MessagePackWriterTypeInfo.WriteInt32); 250 | 251 | var loopEnd = il.DefineLabel(); 252 | 253 | // switch-case (offset += resolver.GetFormatter.Serialize(with cast) 254 | var switchLabels = infos.Select(x => new { Label = il.DefineLabel(), Info = x }).ToArray(); 255 | if (ti.IsClass) 256 | { 257 | argValue.EmitLdarg(); 258 | } 259 | else 260 | { 261 | argValue.EmitLdarga(); 262 | } 263 | il.EmitCall(tag); 264 | il.Emit(OpCodes.Switch, switchLabels.Select(x => x.Label).ToArray()); 265 | il.Emit(OpCodes.Br, loopEnd); // default 266 | 267 | foreach (var item in switchLabels) 268 | { 269 | il.MarkLabel(item.Label); 270 | EmitSerializeUnionCase( 271 | il, 272 | type, 273 | ti, 274 | UnionSerializationInfo.CreateOrNull(type, item.Info), 275 | stringByteKeysFields[item.Info.Tag], 276 | argWriter, 277 | argValue, 278 | argOptions, 279 | localResolver 280 | ); 281 | il.Emit(OpCodes.Br, loopEnd); 282 | } 283 | 284 | // return; 285 | il.MarkLabel(loopEnd); 286 | il.Emit(OpCodes.Ret); 287 | } 288 | 289 | static void EmitSerializeUnionCase( 290 | ILGenerator il, 291 | Type type, 292 | TypeInfo ti, 293 | UnionSerializationInfo info, 294 | FieldBuilder stringByteKeysField, 295 | ArgumentField argWriter, 296 | ArgumentField argValue, 297 | ArgumentField argOptions, 298 | LocalBuilder localResolver 299 | ) 300 | { 301 | if (info.IsIntKey) 302 | { 303 | // use Array 304 | var maxKey = info.Members.Select(x => x.IntKey).DefaultIfEmpty(-1).Max(); 305 | var intKeyMap = info.Members.ToDictionary(x => x.IntKey); 306 | 307 | var len = maxKey + 1; 308 | argWriter.EmitLoad(); 309 | il.EmitLdc_I4(len); 310 | il.EmitCall(MessagePackWriterTypeInfo.WriteArrayHeader); 311 | 312 | var index = 0; 313 | for (int i = 0; i <= maxKey; i++) 314 | { 315 | UnionSerializationInfo.EmittableMember member; 316 | if (intKeyMap.TryGetValue(i, out member)) 317 | { 318 | EmitSerializeValue(il, ti, member, index++, argWriter, argValue, argOptions, localResolver); 319 | } 320 | else 321 | { 322 | // Write Nil as Blanc 323 | argWriter.EmitLoad(); 324 | il.EmitCall(MessagePackWriterTypeInfo.WriteNil); 325 | } 326 | } 327 | } 328 | else 329 | { 330 | // use Map 331 | var writeCount = info.Members.Count(); 332 | 333 | argWriter.EmitLoad(); 334 | il.EmitLdc_I4(writeCount); 335 | ////if (writeCount <= MessagePackRange.MaxFixMapCount) 336 | ////{ 337 | //// il.EmitCall(MessagePackWriterTypeInfo.WriteFixedMapHeaderUnsafe); 338 | ////} 339 | ////else 340 | { 341 | il.EmitCall(MessagePackWriterTypeInfo.WriteMapHeader); 342 | } 343 | 344 | var index = 0; 345 | foreach (UnionSerializationInfo.EmittableMember item in info.Members) 346 | { 347 | argWriter.EmitLoad(); 348 | il.EmitLoadThis(); 349 | il.EmitLdfld(stringByteKeysField); 350 | il.EmitLdc_I4(index); 351 | il.Emit(OpCodes.Ldelem_Ref); 352 | il.Emit(OpCodes.Call, ReadOnlySpanFromByteArray); // convert byte[] to ReadOnlySpan 353 | 354 | // Optimize, WriteRaw(Unity, large) or UnsafeMemory32/64.WriteRawX 355 | var valueLen = CodeGenHelpers.GetEncodedStringBytes(item.StringKey).Length; 356 | if (valueLen <= MessagePackRange.MaxFixStringLength) 357 | { 358 | if (UnsafeMemory.Is32Bit) 359 | { 360 | il.EmitCall(typeof(UnsafeMemory32).GetRuntimeMethod("WriteRaw" + valueLen, new[] { typeof(MessagePackWriter).MakeByRefType(), typeof(ReadOnlySpan) })); 361 | } 362 | else 363 | { 364 | il.EmitCall(typeof(UnsafeMemory64).GetRuntimeMethod("WriteRaw" + valueLen, new[] { typeof(MessagePackWriter).MakeByRefType(), typeof(ReadOnlySpan) })); 365 | } 366 | } 367 | else 368 | { 369 | il.EmitCall(MessagePackWriterTypeInfo.WriteRaw); 370 | } 371 | 372 | EmitSerializeValue(il, type.GetTypeInfo(), item, index, argWriter, argValue, argOptions, localResolver); 373 | index++; 374 | } 375 | } 376 | } 377 | 378 | static void EmitSerializeValue( 379 | ILGenerator il, 380 | TypeInfo type, 381 | UnionSerializationInfo.EmittableMember member, 382 | int index, 383 | ArgumentField argWriter, 384 | ArgumentField argValue, 385 | ArgumentField argOptions, 386 | LocalBuilder localResolver 387 | ) 388 | { 389 | Label endLabel = il.DefineLabel(); 390 | Type t = member.Type; 391 | if (IsOptimizeTargetType(t)) 392 | { 393 | if (!t.GetTypeInfo().IsValueType) 394 | { 395 | // As a nullable type (e.g. byte[] and string) we need to call WriteNil for null values. 396 | Label writeNonNilValueLabel = il.DefineLabel(); 397 | LocalBuilder memberValue = il.DeclareLocal(t); 398 | argValue.EmitLoad(); 399 | member.EmitLoadValue(il); 400 | il.Emit(OpCodes.Dup); 401 | il.EmitStloc(memberValue); 402 | il.Emit(OpCodes.Brtrue, writeNonNilValueLabel); 403 | argWriter.EmitLoad(); 404 | il.EmitCall(MessagePackWriterTypeInfo.WriteNil); 405 | il.Emit(OpCodes.Br, endLabel); 406 | 407 | il.MarkLabel(writeNonNilValueLabel); 408 | argWriter.EmitLoad(); 409 | il.EmitLdloc(memberValue); 410 | } 411 | else 412 | { 413 | argWriter.EmitLoad(); 414 | argValue.EmitLoad(); 415 | member.EmitLoadValue(il); 416 | } 417 | 418 | if (t == typeof(byte[])) 419 | { 420 | il.EmitCall(ReadOnlySpanFromByteArray); 421 | il.EmitCall(MessagePackWriterTypeInfo.WriteBytes); 422 | } 423 | else 424 | { 425 | il.EmitCall(typeof(MessagePackWriter).GetRuntimeMethod("Write", new Type[] { t })); 426 | } 427 | } 428 | else 429 | { 430 | il.EmitLdloc(localResolver); 431 | il.Emit(OpCodes.Call, getFormatterWithVerify.MakeGenericMethod(t)); 432 | 433 | argWriter.EmitLoad(); 434 | argValue.EmitLoad(); 435 | member.EmitLoadValue(il); 436 | argOptions.EmitLoad(); 437 | il.EmitCall(getSerialize(t)); 438 | } 439 | 440 | il.MarkLabel(endLabel); 441 | } 442 | 443 | // T Deserialize([arg:1]ref MessagePackReader reader, [arg:2]MessagePackSerializerOptions options); 444 | static void BuildDeserialize( 445 | Type type, 446 | UnionCaseInfo[] infos, 447 | MethodBuilder method, 448 | FieldBuilder[] stringByteKeysFields, 449 | ILGenerator il, 450 | int firstArgIndex 451 | ) 452 | { 453 | var ti = type.GetTypeInfo(); 454 | 455 | var reader = new ArgumentField(il, firstArgIndex, @ref: true); 456 | var argOptions = new ArgumentField(il, firstArgIndex + 1); 457 | 458 | // if(MessagePackBinary.TryReadNil()) { return null; } 459 | Label falseLabel = il.DefineLabel(); 460 | reader.EmitLdarg(); 461 | il.EmitCall(MessagePackReaderTypeInfo.TryReadNil); 462 | il.Emit(OpCodes.Brfalse_S, falseLabel); 463 | 464 | if (ti.IsClass) 465 | { 466 | il.Emit(OpCodes.Ldnull); 467 | il.Emit(OpCodes.Ret); 468 | } 469 | else 470 | { 471 | il.Emit(OpCodes.Ldstr, "typecode is null, struct not supported"); 472 | il.Emit(OpCodes.Newobj, invalidOperationExceptionConstructor); 473 | il.Emit(OpCodes.Throw); 474 | } 475 | 476 | il.MarkLabel(falseLabel); 477 | 478 | // IFormatterResolver resolver = options.Resolver; 479 | LocalBuilder localResolver = il.DeclareLocal(typeof(IFormatterResolver)); 480 | argOptions.EmitLdarg(); 481 | il.EmitCall(getResolverFromOptions); 482 | il.EmitStloc(localResolver); 483 | 484 | // read-array header and validate, reader.ReadArrayHeader() != 2) throw; 485 | Label rightLabel = il.DefineLabel(); 486 | reader.EmitLdarg(); 487 | il.EmitCall(MessagePackReaderTypeInfo.ReadArrayHeader); 488 | il.EmitLdc_I4(2); 489 | il.Emit(OpCodes.Beq_S, rightLabel); 490 | il.Emit(OpCodes.Ldstr, "Invalid Union data was detected. Type:" + type.FullName); 491 | il.Emit(OpCodes.Newobj, invalidOperationExceptionConstructor); 492 | il.Emit(OpCodes.Throw); 493 | 494 | il.MarkLabel(rightLabel); 495 | 496 | // read key 497 | LocalBuilder key = il.DeclareLocal(typeof(int)); 498 | reader.EmitLdarg(); 499 | il.EmitCall(MessagePackReaderTypeInfo.ReadInt32); 500 | il.EmitStloc(key); 501 | 502 | // switch->read 503 | LocalBuilder result = il.DeclareLocal(type); 504 | Label loopEnd = il.DefineLabel(); 505 | if(ti.IsClass) 506 | { 507 | il.Emit(OpCodes.Ldnull); 508 | il.EmitStloc(result); 509 | } 510 | il.Emit(OpCodes.Ldloc, key); 511 | 512 | var switchLabels = infos.Select(x => new { Label = il.DefineLabel(), Info = x }).ToArray(); 513 | il.Emit(OpCodes.Switch, switchLabels.Select(x => x.Label).ToArray()); 514 | 515 | // default 516 | reader.EmitLdarg(); 517 | il.EmitCall(MessagePackReaderTypeInfo.Skip); 518 | il.Emit(OpCodes.Br, loopEnd); 519 | 520 | foreach (var item in switchLabels) 521 | { 522 | il.MarkLabel(item.Label); 523 | EmitDeserializeUnionCase( 524 | il, 525 | type, 526 | UnionSerializationInfo.CreateOrNull(type, item.Info), 527 | key, 528 | stringByteKeysFields[item.Info.Tag], 529 | reader, 530 | argOptions, 531 | localResolver 532 | ); 533 | il.Emit(OpCodes.Stloc, result); 534 | il.Emit(OpCodes.Br, loopEnd); 535 | } 536 | 537 | il.MarkLabel(loopEnd); 538 | 539 | il.Emit(OpCodes.Ldloc, result); 540 | il.Emit(OpCodes.Ret); 541 | } 542 | 543 | static void EmitDeserializeUnionCase( 544 | ILGenerator il, 545 | Type type, 546 | UnionSerializationInfo info, 547 | LocalBuilder unionKey, 548 | FieldBuilder stringByteKeysField, 549 | ArgumentField reader, 550 | ArgumentField argOptions, 551 | LocalBuilder localResolver 552 | ) 553 | { 554 | // options.Security.DepthStep(ref reader); 555 | argOptions.EmitLoad(); 556 | il.EmitCall(getSecurityFromOptions); 557 | reader.EmitLdarg(); 558 | il.EmitCall(securityDepthStep); 559 | 560 | // var length = ReadMapHeader(ref byteSequence); 561 | LocalBuilder length = il.DeclareLocal(typeof(int)); // [loc:1] 562 | reader.EmitLdarg(); 563 | 564 | if (info.IsIntKey) 565 | { 566 | il.EmitCall(MessagePackReaderTypeInfo.ReadArrayHeader); 567 | } 568 | else 569 | { 570 | il.EmitCall(MessagePackReaderTypeInfo.ReadMapHeader); 571 | } 572 | 573 | il.EmitStloc(length); 574 | 575 | // make local fields 576 | Label? gotoDefault = null; 577 | DeserializeInfo[] infoList; 578 | if (info.IsIntKey) 579 | { 580 | var maxKey = info.Members.Select(x => x.IntKey).DefaultIfEmpty(-1).Max(); 581 | var len = maxKey + 1; 582 | var intKeyMap = info.Members.ToDictionary(x => x.IntKey); 583 | 584 | infoList = Enumerable.Range(0, len) 585 | .Select(x => 586 | { 587 | UnionSerializationInfo.EmittableMember member; 588 | if (intKeyMap.TryGetValue(x, out member)) 589 | { 590 | return new DeserializeInfo 591 | { 592 | MemberInfo = member, 593 | LocalField = il.DeclareLocal(member.Type), 594 | SwitchLabel = il.DefineLabel() 595 | }; 596 | } 597 | else 598 | { 599 | // return null MemberInfo, should filter null 600 | if (gotoDefault == null) 601 | { 602 | gotoDefault = il.DefineLabel(); 603 | } 604 | return new DeserializeInfo 605 | { 606 | MemberInfo = null, 607 | LocalField = null, 608 | SwitchLabel = gotoDefault.Value, 609 | }; 610 | } 611 | }) 612 | .ToArray(); 613 | } 614 | else 615 | { 616 | infoList = info.Members 617 | .Select(item => new DeserializeInfo 618 | { 619 | MemberInfo = item, 620 | LocalField = il.DeclareLocal(item.Type), 621 | SwitchLabel = il.DefineLabel() 622 | }) 623 | .ToArray(); 624 | } 625 | 626 | // Read Loop(for var i = 0; i< length; i++) 627 | if (info.IsStringKey) 628 | { 629 | var automata = new AutomataDictionary(); 630 | for (int i = 0; i < info.Members.Length; i++) 631 | { 632 | automata.Add(info.Members[i].StringKey, i); 633 | } 634 | 635 | LocalBuilder buffer = il.DeclareLocal(typeof(ReadOnlySpan)); 636 | LocalBuilder longKey = il.DeclareLocal(typeof(ulong)); 637 | 638 | // for (int i = 0; i < len; i++) 639 | il.EmitIncrementFor(length, forILocal => 640 | { 641 | Label readNext = il.DefineLabel(); 642 | Label loopEnd = il.DefineLabel(); 643 | 644 | reader.EmitLdarg(); 645 | il.EmitCall(ReadStringSpan); 646 | il.EmitStloc(buffer); 647 | 648 | // gen automata name lookup 649 | automata.EmitMatch( 650 | il, 651 | buffer, 652 | longKey, 653 | x => 654 | { 655 | var i = x.Value; 656 | if (infoList[i].MemberInfo != null) 657 | { 658 | EmitDeserializeValue(il, infoList[i], i, reader, argOptions, localResolver); 659 | il.Emit(OpCodes.Br, loopEnd); 660 | } 661 | else 662 | { 663 | il.Emit(OpCodes.Br, readNext); 664 | } 665 | }, 666 | () => 667 | { 668 | il.Emit(OpCodes.Br, readNext); 669 | }); 670 | 671 | il.MarkLabel(readNext); 672 | reader.EmitLdarg(); 673 | il.EmitCall(MessagePackReaderTypeInfo.Skip); 674 | 675 | il.MarkLabel(loopEnd); 676 | }); 677 | } 678 | else 679 | { 680 | LocalBuilder key = il.DeclareLocal(typeof(int)); 681 | Label switchDefault = il.DefineLabel(); 682 | 683 | il.EmitIncrementFor(length, forILocal => 684 | { 685 | Label loopEnd = il.DefineLabel(); 686 | 687 | il.EmitLdloc(forILocal); 688 | il.EmitStloc(key); 689 | 690 | // switch... local = Deserialize 691 | il.EmitLdloc(key); 692 | 693 | il.Emit(OpCodes.Switch, infoList.Select(x => x.SwitchLabel).ToArray()); 694 | 695 | il.MarkLabel(switchDefault); 696 | 697 | // default, only read. reader.ReadNextBlock(); 698 | reader.EmitLdarg(); 699 | il.EmitCall(MessagePackReaderTypeInfo.Skip); 700 | il.Emit(OpCodes.Br, loopEnd); 701 | 702 | if (gotoDefault != null) 703 | { 704 | il.MarkLabel(gotoDefault.Value); 705 | il.Emit(OpCodes.Br, switchDefault); 706 | } 707 | 708 | var i = 0; 709 | foreach (DeserializeInfo item in infoList) 710 | { 711 | if (item.MemberInfo != null) 712 | { 713 | il.MarkLabel(item.SwitchLabel); 714 | EmitDeserializeValue(il, item, i++, reader, argOptions, localResolver); 715 | il.Emit(OpCodes.Br, loopEnd); 716 | } 717 | } 718 | 719 | il.MarkLabel(loopEnd); 720 | }); 721 | } 722 | 723 | // create result union case 724 | LocalBuilder structLocal = EmitNewObject(il, type, info, infoList); 725 | 726 | // IMessagePackSerializationCallbackReceiver.OnAfterDeserialize() 727 | if (type.GetTypeInfo().ImplementedInterfaces.Any(x => x == typeof(IMessagePackSerializationCallbackReceiver))) 728 | { 729 | // call directly 730 | var runtimeMethods = type.GetRuntimeMethods().Where(x => x.Name == "OnAfterDeserialize").ToArray(); 731 | if (runtimeMethods.Length == 1) 732 | { 733 | if (info.IsClass) 734 | { 735 | il.Emit(OpCodes.Dup); 736 | } 737 | else 738 | { 739 | il.EmitLdloca(structLocal); 740 | } 741 | 742 | il.Emit(OpCodes.Call, runtimeMethods[0]); // don't use EmitCall helper(must use 'Call') 743 | } 744 | else 745 | { 746 | if (info.IsStruct) 747 | { 748 | il.EmitLdloc(structLocal); 749 | il.Emit(OpCodes.Box, type); 750 | } 751 | else 752 | { 753 | il.Emit(OpCodes.Dup); 754 | } 755 | il.EmitCall(onAfterDeserialize); 756 | } 757 | } 758 | 759 | // reader.Depth--; 760 | reader.EmitLdarg(); 761 | il.Emit(OpCodes.Dup); 762 | il.EmitCall(readerDepthGet); 763 | il.Emit(OpCodes.Ldc_I4_1); 764 | il.Emit(OpCodes.Sub_Ovf); 765 | il.EmitCall(readerDepthSet); 766 | 767 | if (info.IsStruct) 768 | { 769 | il.Emit(OpCodes.Ldloc, structLocal); 770 | } 771 | } 772 | 773 | static void EmitDeserializeValue( 774 | ILGenerator il, 775 | DeserializeInfo info, 776 | int index, 777 | ArgumentField argReader, 778 | ArgumentField argOptions, 779 | LocalBuilder localResolver 780 | ) 781 | { 782 | Label storeLabel = il.DefineLabel(); 783 | UnionSerializationInfo.EmittableMember member = info.MemberInfo; 784 | Type t = member.Type; 785 | if (IsOptimizeTargetType(t)) 786 | { 787 | if (!t.GetTypeInfo().IsValueType) 788 | { 789 | // As a nullable type (e.g. byte[] and string) we need to first call TryReadNil 790 | // if (reader.TryReadNil()) 791 | Label readNonNilValueLabel = il.DefineLabel(); 792 | argReader.EmitLdarg(); 793 | il.EmitCall(MessagePackReaderTypeInfo.TryReadNil); 794 | il.Emit(OpCodes.Brfalse_S, readNonNilValueLabel); 795 | il.Emit(OpCodes.Ldnull); 796 | il.Emit(OpCodes.Br, storeLabel); 797 | 798 | il.MarkLabel(readNonNilValueLabel); 799 | } 800 | 801 | argReader.EmitLdarg(); 802 | if (t == typeof(byte[])) 803 | { 804 | LocalBuilder local = il.DeclareLocal(typeof(ReadOnlySequence?)); 805 | il.EmitCall(MessagePackReaderTypeInfo.ReadBytes); 806 | il.EmitStloc(local); 807 | il.EmitLdloca(local); 808 | il.EmitCall(ArrayFromNullableReadOnlySequence); 809 | } 810 | else 811 | { 812 | il.EmitCall(MessagePackReaderTypeInfo.TypeInfo.GetDeclaredMethods("Read" + t.Name).First(x => x.GetParameters().Length == 0)); 813 | } 814 | } 815 | else 816 | { 817 | il.EmitLdloc(localResolver); 818 | il.EmitCall(getFormatterWithVerify.MakeGenericMethod(t)); 819 | argReader.EmitLdarg(); 820 | argOptions.EmitLoad(); 821 | il.EmitCall(getDeserialize(t)); 822 | } 823 | 824 | il.MarkLabel(storeLabel); 825 | il.EmitStloc(info.LocalField); 826 | } 827 | 828 | private static LocalBuilder EmitNewObject(ILGenerator il, Type type, UnionSerializationInfo info, DeserializeInfo[] members) 829 | { 830 | if (info.IsClass) 831 | { 832 | foreach (UnionSerializationInfo.EmittableMember item in info.MethodParameters) 833 | { 834 | DeserializeInfo local = members.First(x => x.MemberInfo == item); 835 | il.EmitLdloc(local.LocalField); 836 | } 837 | 838 | il.Emit(OpCodes.Call, info.NewMethod); 839 | 840 | return null; 841 | } 842 | else 843 | { 844 | LocalBuilder result = il.DeclareLocal(type); 845 | foreach (var item in info.MethodParameters) 846 | { 847 | var local = members.First(x => x.MemberInfo == item); 848 | il.EmitLdloc(local.LocalField); 849 | } 850 | 851 | il.Emit(OpCodes.Call, info.NewMethod); 852 | il.Emit(OpCodes.Stloc, result); 853 | 854 | return result; // struct returns local result field 855 | } 856 | } 857 | 858 | private static bool IsOptimizeTargetType(Type type) 859 | { 860 | return type == typeof(Int16) 861 | || type == typeof(Int32) 862 | || type == typeof(Int64) 863 | || type == typeof(UInt16) 864 | || type == typeof(UInt32) 865 | || type == typeof(UInt64) 866 | || type == typeof(Single) 867 | || type == typeof(Double) 868 | || type == typeof(bool) 869 | || type == typeof(byte) 870 | || type == typeof(sbyte) 871 | || type == typeof(char) 872 | || type == typeof(byte[]) 873 | 874 | // Do not include types that resolvers are allowed to modify. 875 | ////|| type == typeof(DateTime) // OldSpec has no support, so for that and perf reasons a .NET native DateTime resolver exists. 876 | ////|| type == typeof(string) // https://github.com/Cysharp/MasterMemory provides custom formatter for string interning. 877 | ; 878 | } 879 | 880 | // EmitInfos... 881 | private static readonly Type refMessagePackReader = typeof(MessagePackReader).MakeByRefType(); 882 | 883 | private static readonly MethodInfo ReadOnlySpanFromByteArray = typeof(ReadOnlySpan).GetRuntimeMethod("op_Implicit", new[] { typeof(byte[]) }); 884 | private static readonly MethodInfo ReadStringSpan = typeof(CodeGenHelpers).GetRuntimeMethod(nameof(CodeGenHelpers.ReadStringSpan), new[] { typeof(MessagePackReader).MakeByRefType() }); 885 | private static readonly MethodInfo ArrayFromNullableReadOnlySequence = typeof(CodeGenHelpers).GetRuntimeMethod(nameof(CodeGenHelpers.GetArrayFromNullableSequence), new[] { typeof(ReadOnlySequence?).MakeByRefType() }); 886 | 887 | private static readonly MethodInfo getFormatterWithVerify = typeof(FormatterResolverExtensions).GetRuntimeMethods().First(x => x.Name == nameof(FormatterResolverExtensions.GetFormatterWithVerify)); 888 | private static readonly MethodInfo getResolverFromOptions = typeof(MessagePackSerializerOptions).GetRuntimeProperty(nameof(MessagePackSerializerOptions.Resolver)).GetMethod; 889 | private static readonly MethodInfo getSecurityFromOptions = typeof(MessagePackSerializerOptions).GetRuntimeProperty(nameof(MessagePackSerializerOptions.Security)).GetMethod; 890 | private static readonly MethodInfo securityDepthStep = typeof(MessagePackSecurity).GetRuntimeMethod(nameof(MessagePackSecurity.DepthStep), new[] { typeof(MessagePackReader).MakeByRefType() }); 891 | private static readonly MethodInfo readerDepthGet = typeof(MessagePackReader).GetRuntimeProperty(nameof(MessagePackReader.Depth)).GetMethod; 892 | private static readonly MethodInfo readerDepthSet = typeof(MessagePackReader).GetRuntimeProperty(nameof(MessagePackReader.Depth)).SetMethod; 893 | private static readonly Func getSerialize = t => typeof(IMessagePackFormatter<>).MakeGenericType(t).GetRuntimeMethod(nameof(IMessagePackFormatter.Serialize), new[] { typeof(MessagePackWriter).MakeByRefType(), t, typeof(MessagePackSerializerOptions) }); 894 | private static readonly Func getDeserialize = t => typeof(IMessagePackFormatter<>).MakeGenericType(t).GetRuntimeMethod(nameof(IMessagePackFormatter.Deserialize), new[] { refMessagePackReader, typeof(MessagePackSerializerOptions) }); 895 | //// static readonly ConstructorInfo dictionaryConstructor = typeof(ByteArrayStringHashTable).GetTypeInfo().DeclaredConstructors.First(x => { var p = x.GetParameters(); return p.Length == 1 && p[0].ParameterType == typeof(int); }); 896 | //// static readonly MethodInfo dictionaryAdd = typeof(ByteArrayStringHashTable).GetRuntimeMethod("Add", new[] { typeof(string), typeof(int) }); 897 | //// static readonly MethodInfo dictionaryTryGetValue = typeof(ByteArrayStringHashTable).GetRuntimeMethod("TryGetValue", new[] { typeof(ArraySegment), refInt }); 898 | private static readonly ConstructorInfo messagePackSerializationExceptionMessageOnlyConstructor = typeof(MessagePackSerializationException).GetTypeInfo().DeclaredConstructors.First(x => 899 | { 900 | ParameterInfo[] p = x.GetParameters(); 901 | return p.Length == 1 && p[0].ParameterType == typeof(string); 902 | }); 903 | 904 | static readonly ConstructorInfo invalidOperationExceptionConstructor = typeof(System.InvalidOperationException).GetTypeInfo().DeclaredConstructors.First(x => { var p = x.GetParameters(); return p.Length == 1 && p[0].ParameterType == typeof(string); }); 905 | static readonly Type refUnionCaseInfo = typeof(Microsoft.FSharp.Reflection.UnionCaseInfo).MakeByRefType(); 906 | 907 | static readonly ConstructorInfo objectCtor = typeof(object).GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Length == 0); 908 | 909 | static readonly Func getTag = type => type.GetTypeInfo().GetProperty("Tag").GetGetMethod(); 910 | 911 | static readonly MethodInfo onBeforeSerialize = typeof(IMessagePackSerializationCallbackReceiver).GetRuntimeMethod("OnBeforeSerialize", Type.EmptyTypes); 912 | static readonly MethodInfo onAfterDeserialize = typeof(IMessagePackSerializationCallbackReceiver).GetRuntimeMethod("OnAfterDeserialize", Type.EmptyTypes); 913 | 914 | internal static class MessagePackWriterTypeInfo 915 | { 916 | internal static readonly TypeInfo TypeInfo = typeof(MessagePackWriter).GetTypeInfo(); 917 | 918 | internal static readonly MethodInfo WriteMapHeader = typeof(MessagePackWriter).GetRuntimeMethod(nameof(MessagePackWriter.WriteMapHeader), new[] { typeof(int) }); 919 | internal static readonly MethodInfo WriteArrayHeader = typeof(MessagePackWriter).GetRuntimeMethod(nameof(MessagePackWriter.WriteArrayHeader), new[] { typeof(int) }); 920 | internal static readonly MethodInfo WriteBytes = typeof(MessagePackWriter).GetRuntimeMethod(nameof(MessagePackWriter.Write), new[] { typeof(ReadOnlySpan) }); 921 | internal static readonly MethodInfo WriteNil = typeof(MessagePackWriter).GetRuntimeMethod(nameof(MessagePackWriter.WriteNil), Type.EmptyTypes); 922 | internal static readonly MethodInfo WriteRaw = typeof(MessagePackWriter).GetRuntimeMethod(nameof(MessagePackWriter.WriteRaw), new[] { typeof(ReadOnlySpan) }); 923 | internal static readonly MethodInfo WriteInt32 = typeof(MessagePackWriter).GetRuntimeMethod(nameof(MessagePackWriter.Write), new[] { typeof(int) }); 924 | } 925 | 926 | internal static class MessagePackReaderTypeInfo 927 | { 928 | internal static readonly TypeInfo TypeInfo = typeof(MessagePackReader).GetTypeInfo(); 929 | 930 | internal static readonly MethodInfo ReadArrayHeader = typeof(MessagePackReader).GetRuntimeMethod(nameof(MessagePackReader.ReadArrayHeader), Type.EmptyTypes); 931 | internal static readonly MethodInfo ReadMapHeader = typeof(MessagePackReader).GetRuntimeMethod(nameof(MessagePackReader.ReadMapHeader), Type.EmptyTypes); 932 | internal static readonly MethodInfo ReadBytes = typeof(MessagePackReader).GetRuntimeMethod(nameof(MessagePackReader.ReadBytes), Type.EmptyTypes); 933 | internal static readonly MethodInfo TryReadNil = typeof(MessagePackReader).GetRuntimeMethod(nameof(MessagePackReader.TryReadNil), Type.EmptyTypes); 934 | internal static readonly MethodInfo Skip = typeof(MessagePackReader).GetRuntimeMethod(nameof(MessagePackReader.Skip), Type.EmptyTypes); 935 | internal static readonly MethodInfo ReadInt32 = typeof(MessagePackReader).GetRuntimeMethod(nameof(MessagePackReader.ReadInt32), Type.EmptyTypes); 936 | } 937 | 938 | internal static class CodeGenHelpersTypeInfo 939 | { 940 | public static readonly MethodInfo GetEncodedStringBytes = typeof(CodeGenHelpers).GetRuntimeMethod(nameof(CodeGenHelpers.GetEncodedStringBytes), new[] { typeof(string) }); 941 | } 942 | 943 | class DeserializeInfo 944 | { 945 | public UnionSerializationInfo.EmittableMember MemberInfo { get; set; } 946 | public LocalBuilder LocalField { get; set; } 947 | public Label SwitchLabel { get; set; } 948 | } 949 | } 950 | } 951 | 952 | namespace MessagePack.FSharp.Internal 953 | { 954 | internal class UnionSerializationInfo 955 | { 956 | public bool IsIntKey { get; set; } 957 | public bool IsStringKey { get { return !IsIntKey; } } 958 | public bool IsClass { get; set; } 959 | public bool IsStruct { get { return !IsClass; } } 960 | public MethodInfo NewMethod { get; set; } 961 | public EmittableMember[] MethodParameters { get; set; } 962 | public EmittableMember[] Members { get; set; } 963 | 964 | UnionSerializationInfo() { } 965 | 966 | public static UnionSerializationInfo CreateOrNull(Type type, UnionCaseInfo caseInfo) 967 | { 968 | type = caseInfo.DeclaringType; 969 | 970 | var ti = type.GetTypeInfo(); 971 | var isClass = ti.IsClass; 972 | 973 | var contractAttr = ti.GetCustomAttribute(); 974 | 975 | var isIntKey = true; 976 | var intMembers = new Dictionary(); 977 | var stringMembers = new Dictionary(); 978 | 979 | if (contractAttr == null || contractAttr.KeyAsPropertyName) 980 | { 981 | isIntKey = false; 982 | 983 | var hiddenIntKey = 0; 984 | foreach (var item in caseInfo.GetFields()) 985 | { 986 | var member = new EmittableMember 987 | { 988 | PropertyInfo = item, 989 | StringKey = item.Name, 990 | IntKey = hiddenIntKey++ 991 | }; 992 | stringMembers.Add(member.StringKey, member); 993 | } 994 | } 995 | else 996 | { 997 | var hiddenIntKey = 0; 998 | foreach (var item in caseInfo.GetFields()) 999 | { 1000 | 1001 | var member = new EmittableMember 1002 | { 1003 | PropertyInfo = item, 1004 | IntKey = hiddenIntKey++ 1005 | }; 1006 | intMembers.Add(member.IntKey, member); 1007 | } 1008 | } 1009 | 1010 | MethodInfo method; 1011 | var methodParameters = new List(); 1012 | 1013 | if (caseInfo.GetFields().Any()) 1014 | { 1015 | method = ti.GetMethod("New" + caseInfo.Name, BindingFlags.Static | BindingFlags.Public); 1016 | if (method == null) throw new MessagePackDynamicUnionResolverException("can't find public method. case:" + caseInfo.Name); 1017 | 1018 | var methodLookupDictionary = stringMembers.ToLookup(x => x.Key, x => x, StringComparer.OrdinalIgnoreCase); 1019 | 1020 | var methodParamIndex = 0; 1021 | foreach (var item in method.GetParameters()) 1022 | { 1023 | EmittableMember paramMember; 1024 | if (isIntKey) 1025 | { 1026 | if (intMembers.TryGetValue(methodParamIndex, out paramMember)) 1027 | { 1028 | if (item.ParameterType == paramMember.Type) 1029 | { 1030 | methodParameters.Add(paramMember); 1031 | } 1032 | else 1033 | { 1034 | throw new MessagePackDynamicUnionResolverException("can't find matched method parameter, parameterType mismatch. case:" + caseInfo.Name + " parameterIndex:" + methodParamIndex + " paramterType:" + item.ParameterType.Name); 1035 | } 1036 | } 1037 | else 1038 | { 1039 | throw new MessagePackDynamicUnionResolverException("can't find matched method parameter, index not found. case:" + caseInfo.Name + " parameterIndex:" + methodParamIndex); 1040 | } 1041 | } 1042 | else 1043 | { 1044 | var hasKey = methodLookupDictionary[item.Name]; 1045 | var len = hasKey.Count(); 1046 | if (len != 0) 1047 | { 1048 | if (len != 1) 1049 | { 1050 | throw new MessagePackDynamicUnionResolverException("duplicate matched method parameter name:" + caseInfo.Name + " parameterName:" + item.Name + " paramterType:" + item.ParameterType.Name); 1051 | } 1052 | 1053 | paramMember = hasKey.First().Value; 1054 | if (item.ParameterType == paramMember.Type) 1055 | { 1056 | methodParameters.Add(paramMember); 1057 | } 1058 | else 1059 | { 1060 | throw new MessagePackDynamicUnionResolverException("can't find matched method parameter, parameterType mismatch. case:" + caseInfo.Name + " parameterName:" + item.Name + " paramterType:" + item.ParameterType.Name); 1061 | } 1062 | } 1063 | else 1064 | { 1065 | throw new MessagePackDynamicUnionResolverException("can't find matched method parameter, index not found. case:" + caseInfo.Name + " parameterName:" + item.Name); 1066 | } 1067 | } 1068 | methodParamIndex++; 1069 | } 1070 | } 1071 | else 1072 | { 1073 | method = ti.GetProperty(caseInfo.Name, BindingFlags.Public | BindingFlags.Static).GetGetMethod(); 1074 | } 1075 | 1076 | return new UnionSerializationInfo 1077 | { 1078 | IsClass = isClass, 1079 | NewMethod = method, 1080 | MethodParameters = methodParameters.ToArray(), 1081 | IsIntKey = isIntKey, 1082 | Members = (isIntKey) ? intMembers.Values.ToArray() : stringMembers.Values.ToArray() 1083 | }; 1084 | } 1085 | 1086 | public class EmittableMember 1087 | { 1088 | public int IntKey { get; set; } 1089 | public string StringKey { get; set; } 1090 | public Type Type { get { return PropertyInfo.PropertyType; } } 1091 | public PropertyInfo PropertyInfo { get; set; } 1092 | public bool IsValueType 1093 | { 1094 | get 1095 | { 1096 | return ((MemberInfo)PropertyInfo).DeclaringType.GetTypeInfo().IsValueType; 1097 | } 1098 | } 1099 | 1100 | public void EmitLoadValue(ILGenerator il) 1101 | { 1102 | il.EmitCall(PropertyInfo.GetGetMethod()); 1103 | } 1104 | } 1105 | } 1106 | 1107 | internal class MessagePackDynamicUnionResolverException : Exception 1108 | { 1109 | public MessagePackDynamicUnionResolverException(string message) 1110 | : base(message) 1111 | { 1112 | 1113 | } 1114 | } 1115 | } 1116 | -------------------------------------------------------------------------------- /src/MessagePack.FSharpExtensions/FSharpResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using MessagePack.Formatters; 5 | using Microsoft.FSharp.Core; 6 | using Microsoft.FSharp.Control; 7 | using Microsoft.FSharp.Collections; 8 | using MessagePack.FSharp.Formatters; 9 | 10 | namespace MessagePack.FSharp 11 | { 12 | public sealed class FSharpResolver : IFormatterResolver 13 | { 14 | public static readonly IFormatterResolver Instance = new FSharpResolver(); 15 | 16 | private FSharpResolver() { } 17 | 18 | public IMessagePackFormatter GetFormatter() 19 | { 20 | return FormatterCache.Formatter; 21 | } 22 | 23 | private static class FormatterCache 24 | { 25 | internal static readonly IMessagePackFormatter Formatter; 26 | 27 | static FormatterCache() 28 | { 29 | Formatter = (IMessagePackFormatter)FSharpGetFormatterHelper.GetFormatter(typeof(T)); 30 | 31 | if (Formatter == null) 32 | { 33 | var f = DynamicUnionResolver.Instance.GetFormatter(); 34 | if (f != null) 35 | { 36 | Formatter = f; 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | internal static class FSharpGetFormatterHelper 44 | { 45 | private static readonly Dictionary formatterMap = new Dictionary() 46 | { 47 | {typeof(FSharpList<>), typeof(FSharpListFormatter<>)}, 48 | {typeof(FSharpMap<,>), typeof(FSharpMapFormatter<,>)}, 49 | {typeof(FSharpSet<>), typeof(FSharpSetFormatter<>)}, 50 | {typeof(FSharpAsync<>), typeof(FSharpAsyncFormatter<>)} 51 | }; 52 | 53 | internal static object GetFormatter(Type t) 54 | { 55 | var ti = t.GetTypeInfo(); 56 | 57 | if (t == typeof(Unit)) 58 | { 59 | return new UnitFormatter(); 60 | } 61 | 62 | if (ti.IsGenericType) 63 | { 64 | var genericType = ti.GetGenericTypeDefinition(); 65 | var gti = genericType.GetTypeInfo(); 66 | 67 | Type formatterType; 68 | if (formatterMap.TryGetValue(genericType, out formatterType)) 69 | { 70 | return CreateInstance(formatterType, ti.GenericTypeArguments); 71 | } 72 | else if (gti.IsFSharpOption()) 73 | { 74 | return CreateInstance(typeof(FSharpOptionFormatter<>), new[] { ti.GenericTypeArguments[0] }); 75 | } 76 | else if (gti.IsFSharpValueOption()) 77 | { 78 | return CreateInstance(typeof(FSharpValueOptionFormatter<>), new[] { ti.GenericTypeArguments[0] }); 79 | } 80 | } 81 | 82 | return null; 83 | } 84 | 85 | private static object CreateInstance(Type genericType, Type[] genericTypeArguments, params object[] arguments) 86 | { 87 | return Activator.CreateInstance(genericType.MakeGenericType(genericTypeArguments), arguments); 88 | } 89 | } 90 | 91 | internal static class ReflectionExtensions 92 | { 93 | public static bool IsFSharpOption(this TypeInfo type) 94 | { 95 | return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(FSharpOption<>); 96 | } 97 | 98 | public static bool IsFSharpValueOption(this TypeInfo type) 99 | { 100 | return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(FSharpValueOption<>); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/MessagePack.FSharpExtensions/Formatters/FSharpAsyncFormatter.cs: -------------------------------------------------------------------------------- 1 | using MessagePack.Formatters; 2 | using Microsoft.FSharp.Control; 3 | 4 | namespace MessagePack.FSharp.Formatters 5 | { 6 | public sealed class FSharpAsyncFormatter : IMessagePackFormatter> 7 | { 8 | 9 | public FSharpAsyncFormatter() { } 10 | 11 | public void Serialize(ref MessagePackWriter writer, FSharpAsync value, MessagePackSerializerOptions options) 12 | { 13 | if (value == null) 14 | { 15 | writer.WriteNil(); 16 | return; 17 | } 18 | else 19 | { 20 | var v = FSharpAsync.RunSynchronously(value, null, null); 21 | IFormatterResolver resolver = options.Resolver; 22 | resolver.GetFormatterWithVerify().Serialize(ref writer, v, options); 23 | } 24 | } 25 | 26 | public FSharpAsync Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) 27 | { 28 | if (reader.TryReadNil()) 29 | { 30 | return default; 31 | } 32 | IFormatterResolver resolver = options.Resolver; 33 | options.Security.DepthStep(ref reader); 34 | T value = resolver.GetFormatterWithVerify().Deserialize(ref reader, options);; 35 | reader.Depth--; 36 | return Microsoft.FSharp.Core.ExtraTopLevelOperators.DefaultAsyncBuilder.Return(value); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/MessagePack.FSharpExtensions/Formatters/FSharpCollectionFormatters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MessagePack.Formatters; 4 | using Microsoft.FSharp.Collections; 5 | 6 | namespace MessagePack.FSharp.Formatters 7 | { 8 | public sealed class FSharpListFormatter : CollectionFormatterBase, FSharpList> 9 | { 10 | protected override void Add(T[] collection, int index, T value, MessagePackSerializerOptions options) 11 | { 12 | collection[index] = value; 13 | } 14 | 15 | protected override FSharpList Complete(T[] intermediateCollection) 16 | { 17 | return ListModule.OfArray(intermediateCollection); 18 | } 19 | 20 | protected override T[] Create(int count, MessagePackSerializerOptions options) 21 | { 22 | return new T[count]; 23 | } 24 | 25 | protected override IEnumerator GetSourceEnumerator(FSharpList source) 26 | { 27 | return ((IEnumerable)source).GetEnumerator(); 28 | } 29 | } 30 | 31 | public sealed class FSharpMapFormatter : DictionaryFormatterBase[], IEnumerator>, FSharpMap> 32 | { 33 | protected override void Add(Tuple[] collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) 34 | { 35 | collection[index] = Tuple.Create(key, value); 36 | } 37 | 38 | protected override FSharpMap Complete(Tuple[] intermediateCollection) 39 | { 40 | return MapModule.OfArray(intermediateCollection); 41 | } 42 | 43 | protected override Tuple[] Create(int count, MessagePackSerializerOptions options) 44 | { 45 | return new Tuple[count]; 46 | } 47 | 48 | protected override IEnumerator> GetSourceEnumerator(FSharpMap source) 49 | { 50 | return ((IEnumerable>)source).GetEnumerator(); 51 | } 52 | } 53 | 54 | public sealed class FSharpSetFormatter : CollectionFormatterBase, FSharpSet> 55 | { 56 | protected override void Add(T[] collection, int index, T value, MessagePackSerializerOptions options) 57 | { 58 | collection[index] = value; 59 | } 60 | 61 | protected override FSharpSet Complete(T[] intermediateCollection) 62 | { 63 | return SetModule.OfArray(intermediateCollection); 64 | } 65 | 66 | protected override T[] Create(int count, MessagePackSerializerOptions options) 67 | { 68 | return new T[count]; 69 | } 70 | 71 | protected override IEnumerator GetSourceEnumerator(FSharpSet source) 72 | { 73 | return ((IEnumerable)source).GetEnumerator(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/MessagePack.FSharpExtensions/Formatters/FSharpOptionFormatter.cs: -------------------------------------------------------------------------------- 1 | using MessagePack.Formatters; 2 | using Microsoft.FSharp.Core; 3 | 4 | namespace MessagePack.FSharp.Formatters 5 | { 6 | public sealed class FSharpOptionFormatter : IMessagePackFormatter> 7 | { 8 | public void Serialize(ref MessagePackWriter writer, FSharpOption value, MessagePackSerializerOptions options) 9 | { 10 | if (FSharpOption.get_IsNone(value)) 11 | { 12 | writer.WriteNil(); 13 | return; 14 | } 15 | else 16 | { 17 | IFormatterResolver resolver = options.Resolver; 18 | resolver.GetFormatterWithVerify().Serialize(ref writer, value.Value, options); 19 | } 20 | } 21 | 22 | public FSharpOption Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) 23 | { 24 | if (reader.TryReadNil()) 25 | { 26 | return null; 27 | } 28 | IFormatterResolver resolver = options.Resolver; 29 | options.Security.DepthStep(ref reader); 30 | T value = resolver.GetFormatterWithVerify().Deserialize(ref reader, options);; 31 | reader.Depth--; 32 | return FSharpOption.Some(value); 33 | } 34 | } 35 | 36 | public sealed class FSharpValueOptionFormatter : IMessagePackFormatter> 37 | { 38 | public void Serialize(ref MessagePackWriter writer, FSharpValueOption value, MessagePackSerializerOptions options) 39 | { 40 | if (value.IsNone) 41 | { 42 | writer.WriteNil(); 43 | return; 44 | } 45 | else 46 | { 47 | IFormatterResolver resolver = options.Resolver; 48 | resolver.GetFormatterWithVerify().Serialize(ref writer, value.Value, options); 49 | } 50 | } 51 | 52 | public FSharpValueOption Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) 53 | { 54 | if (reader.TryReadNil()) 55 | { 56 | return FSharpValueOption.None; 57 | } 58 | IFormatterResolver resolver = options.Resolver; 59 | options.Security.DepthStep(ref reader); 60 | T value = resolver.GetFormatterWithVerify().Deserialize(ref reader, options);; 61 | reader.Depth--; 62 | return FSharpValueOption.Some(value); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/MessagePack.FSharpExtensions/Formatters/UnitFormatter.cs: -------------------------------------------------------------------------------- 1 | using MessagePack.Formatters; 2 | using Microsoft.FSharp.Core; 3 | 4 | namespace MessagePack.FSharp.Formatters 5 | { 6 | public sealed class UnitFormatter : IMessagePackFormatter 7 | { 8 | 9 | public UnitFormatter() { } 10 | 11 | public void Serialize(ref MessagePackWriter writer, Unit value, MessagePackSerializerOptions options) 12 | { 13 | writer.WriteNil(); 14 | return; 15 | } 16 | 17 | public Unit Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) 18 | { 19 | reader.ReadNil(); 20 | return null; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MessagePack.FSharpExtensions/Internal/DynamicAssembly.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Yoshifumi Kawai and contributors 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | using System; 22 | using System.Reflection; 23 | using System.Reflection.Emit; 24 | 25 | namespace MessagePack.FSharp.Internal 26 | { 27 | internal class DynamicAssembly 28 | { 29 | readonly AssemblyBuilder assemblyBuilder; 30 | readonly ModuleBuilder moduleBuilder; 31 | 32 | public ModuleBuilder ModuleBuilder { get { return moduleBuilder; } } 33 | 34 | public DynamicAssembly(string moduleName) 35 | { 36 | AssemblyBuilderAccess builderAccess = AssemblyBuilderAccess.Run; 37 | this.assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(moduleName), builderAccess); 38 | this.moduleBuilder = this.assemblyBuilder.DefineDynamicModule(moduleName + ".dll"); 39 | } 40 | 41 | /* requires lock on mono environment. see: https://github.com/neuecc/MessagePack-CSharp/issues/161 */ 42 | 43 | public TypeBuilder DefineType(string name, TypeAttributes attr) => this.moduleBuilder.DefineType(name, attr); 44 | 45 | public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent) => this.moduleBuilder.DefineType(name, attr, parent); 46 | 47 | public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent, Type[] interfaces) => this.moduleBuilder.DefineType(name, attr, parent, interfaces); 48 | } 49 | } -------------------------------------------------------------------------------- /src/MessagePack.FSharpExtensions/Internal/ILGeneratorExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Yoshifumi Kawai 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | using System; 22 | using System.Linq; 23 | using System.Reflection; 24 | using System.Reflection.Emit; 25 | 26 | namespace MessagePack.Internal 27 | { 28 | internal struct ArgumentField 29 | { 30 | private readonly int i; 31 | private readonly bool @ref; 32 | private readonly ILGenerator il; 33 | 34 | public ArgumentField(ILGenerator il, int i, bool @ref = false) 35 | { 36 | this.il = il; 37 | this.i = i; 38 | this.@ref = @ref; 39 | } 40 | 41 | public ArgumentField(ILGenerator il, int i, Type type) 42 | { 43 | this.il = il; 44 | this.i = i; 45 | TypeInfo ti = type.GetTypeInfo(); 46 | this.@ref = (ti.IsClass || ti.IsInterface || ti.IsAbstract) ? false : true; 47 | } 48 | 49 | public void EmitLoad() 50 | { 51 | if (this.@ref) 52 | { 53 | this.il.EmitLdarga(this.i); 54 | } 55 | else 56 | { 57 | this.il.EmitLdarg(this.i); 58 | } 59 | } 60 | 61 | public void EmitLdarg() 62 | { 63 | this.il.EmitLdarg(this.i); 64 | } 65 | 66 | public void EmitLdarga() 67 | { 68 | this.il.EmitLdarga(this.i); 69 | } 70 | 71 | public void EmitStore() 72 | { 73 | this.il.EmitStarg(this.i); 74 | } 75 | } 76 | 77 | /// 78 | /// Provides optimized generation code and helpers. 79 | /// 80 | internal static class ILGeneratorExtensions 81 | { 82 | /// 83 | /// Loads the local variable at a specific index onto the evaluation stack. 84 | /// 85 | public static void EmitLdloc(this ILGenerator il, int index) 86 | { 87 | switch (index) 88 | { 89 | case 0: 90 | il.Emit(OpCodes.Ldloc_0); 91 | break; 92 | case 1: 93 | il.Emit(OpCodes.Ldloc_1); 94 | break; 95 | case 2: 96 | il.Emit(OpCodes.Ldloc_2); 97 | break; 98 | case 3: 99 | il.Emit(OpCodes.Ldloc_3); 100 | break; 101 | default: 102 | if (index <= 255) 103 | { 104 | il.Emit(OpCodes.Ldloc_S, (byte)index); 105 | } 106 | else 107 | { 108 | il.Emit(OpCodes.Ldloc, (short)index); 109 | } 110 | 111 | break; 112 | } 113 | } 114 | 115 | public static void EmitLdloc(this ILGenerator il, LocalBuilder local) 116 | { 117 | EmitLdloc(il, local.LocalIndex); 118 | } 119 | 120 | /// 121 | /// Pops the current value from the top of the evaluation stack and stores it in a the local variable list at a specified index. 122 | /// 123 | public static void EmitStloc(this ILGenerator il, int index) 124 | { 125 | switch (index) 126 | { 127 | case 0: 128 | il.Emit(OpCodes.Stloc_0); 129 | break; 130 | case 1: 131 | il.Emit(OpCodes.Stloc_1); 132 | break; 133 | case 2: 134 | il.Emit(OpCodes.Stloc_2); 135 | break; 136 | case 3: 137 | il.Emit(OpCodes.Stloc_3); 138 | break; 139 | default: 140 | if (index <= 255) 141 | { 142 | il.Emit(OpCodes.Stloc_S, (byte)index); 143 | } 144 | else 145 | { 146 | il.Emit(OpCodes.Stloc, (short)index); 147 | } 148 | 149 | break; 150 | } 151 | } 152 | 153 | public static void EmitStloc(this ILGenerator il, LocalBuilder local) 154 | { 155 | EmitStloc(il, local.LocalIndex); 156 | } 157 | 158 | /// 159 | /// Loads the address of the local variable at a specific index onto the evaluation statck. 160 | /// 161 | public static void EmitLdloca(this ILGenerator il, int index) 162 | { 163 | if (index <= 255) 164 | { 165 | il.Emit(OpCodes.Ldloca_S, (byte)index); 166 | } 167 | else 168 | { 169 | il.Emit(OpCodes.Ldloca, (short)index); 170 | } 171 | } 172 | 173 | public static void EmitLdloca(this ILGenerator il, LocalBuilder local) 174 | { 175 | EmitLdloca(il, local.LocalIndex); 176 | } 177 | 178 | public static void EmitTrue(this ILGenerator il) 179 | { 180 | EmitBoolean(il, true); 181 | } 182 | 183 | public static void EmitFalse(this ILGenerator il) 184 | { 185 | EmitBoolean(il, false); 186 | } 187 | 188 | public static void EmitBoolean(this ILGenerator il, bool value) 189 | { 190 | EmitLdc_I4(il, value ? 1 : 0); 191 | } 192 | 193 | /// 194 | /// Pushes a supplied value of type int32 onto the evaluation stack as an int32. 195 | /// 196 | public static void EmitLdc_I4(this ILGenerator il, int value) 197 | { 198 | switch (value) 199 | { 200 | case -1: 201 | il.Emit(OpCodes.Ldc_I4_M1); 202 | break; 203 | case 0: 204 | il.Emit(OpCodes.Ldc_I4_0); 205 | break; 206 | case 1: 207 | il.Emit(OpCodes.Ldc_I4_1); 208 | break; 209 | case 2: 210 | il.Emit(OpCodes.Ldc_I4_2); 211 | break; 212 | case 3: 213 | il.Emit(OpCodes.Ldc_I4_3); 214 | break; 215 | case 4: 216 | il.Emit(OpCodes.Ldc_I4_4); 217 | break; 218 | case 5: 219 | il.Emit(OpCodes.Ldc_I4_5); 220 | break; 221 | case 6: 222 | il.Emit(OpCodes.Ldc_I4_6); 223 | break; 224 | case 7: 225 | il.Emit(OpCodes.Ldc_I4_7); 226 | break; 227 | case 8: 228 | il.Emit(OpCodes.Ldc_I4_8); 229 | break; 230 | default: 231 | if (value >= -128 && value <= 127) 232 | { 233 | il.Emit(OpCodes.Ldc_I4_S, (sbyte)value); 234 | } 235 | else 236 | { 237 | il.Emit(OpCodes.Ldc_I4, value); 238 | } 239 | 240 | break; 241 | } 242 | } 243 | 244 | public static void EmitUnboxOrCast(this ILGenerator il, Type type) 245 | { 246 | if (type.GetTypeInfo().IsValueType) 247 | { 248 | il.Emit(OpCodes.Unbox_Any, type); 249 | } 250 | else 251 | { 252 | il.Emit(OpCodes.Castclass, type); 253 | } 254 | } 255 | 256 | public static void EmitBoxOrDoNothing(this ILGenerator il, Type type) 257 | { 258 | if (type.GetTypeInfo().IsValueType) 259 | { 260 | il.Emit(OpCodes.Box, type); 261 | } 262 | } 263 | 264 | public static void EmitLdarg(this ILGenerator il, int index) 265 | { 266 | switch (index) 267 | { 268 | case 0: 269 | il.Emit(OpCodes.Ldarg_0); 270 | break; 271 | case 1: 272 | il.Emit(OpCodes.Ldarg_1); 273 | break; 274 | case 2: 275 | il.Emit(OpCodes.Ldarg_2); 276 | break; 277 | case 3: 278 | il.Emit(OpCodes.Ldarg_3); 279 | break; 280 | default: 281 | if (index <= 255) 282 | { 283 | il.Emit(OpCodes.Ldarg_S, (byte)index); 284 | } 285 | else 286 | { 287 | il.Emit(OpCodes.Ldarg, index); 288 | } 289 | 290 | break; 291 | } 292 | } 293 | 294 | public static void EmitLoadThis(this ILGenerator il) 295 | { 296 | EmitLdarg(il, 0); 297 | } 298 | 299 | public static void EmitLdarga(this ILGenerator il, int index) 300 | { 301 | if (index <= 255) 302 | { 303 | il.Emit(OpCodes.Ldarga_S, (byte)index); 304 | } 305 | else 306 | { 307 | il.Emit(OpCodes.Ldarga, index); 308 | } 309 | } 310 | 311 | public static void EmitStarg(this ILGenerator il, int index) 312 | { 313 | if (index <= 255) 314 | { 315 | il.Emit(OpCodes.Starg_S, (byte)index); 316 | } 317 | else 318 | { 319 | il.Emit(OpCodes.Starg, index); 320 | } 321 | } 322 | 323 | /// 324 | /// Helper for Pop op. 325 | /// 326 | public static void EmitPop(this ILGenerator il, int count) 327 | { 328 | for (int i = 0; i < count; i++) 329 | { 330 | il.Emit(OpCodes.Pop); 331 | } 332 | } 333 | 334 | public static void EmitCall(this ILGenerator il, MethodInfo methodInfo) 335 | { 336 | if (methodInfo.IsFinal || !methodInfo.IsVirtual) 337 | { 338 | il.Emit(OpCodes.Call, methodInfo); 339 | } 340 | else 341 | { 342 | il.Emit(OpCodes.Callvirt, methodInfo); 343 | } 344 | } 345 | 346 | public static void EmitLdfld(this ILGenerator il, FieldInfo fieldInfo) 347 | { 348 | il.Emit(OpCodes.Ldfld, fieldInfo); 349 | } 350 | 351 | public static void EmitLdsfld(this ILGenerator il, FieldInfo fieldInfo) 352 | { 353 | il.Emit(OpCodes.Ldsfld, fieldInfo); 354 | } 355 | 356 | public static void EmitRet(this ILGenerator il) 357 | { 358 | il.Emit(OpCodes.Ret); 359 | } 360 | 361 | public static void EmitIntZeroReturn(this ILGenerator il) 362 | { 363 | il.EmitLdc_I4(0); 364 | il.Emit(OpCodes.Ret); 365 | } 366 | 367 | public static void EmitNullReturn(this ILGenerator il) 368 | { 369 | il.Emit(OpCodes.Ldnull); 370 | il.Emit(OpCodes.Ret); 371 | } 372 | 373 | public static void EmitULong(this ILGenerator il, ulong value) 374 | { 375 | il.Emit(OpCodes.Ldc_I8, unchecked((long)value)); 376 | } 377 | 378 | public static void EmitThrowNotimplemented(this ILGenerator il) 379 | { 380 | il.Emit(OpCodes.Newobj, typeof(System.NotImplementedException).GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Length == 0)); 381 | il.Emit(OpCodes.Throw); 382 | } 383 | 384 | /// for var i = 0, i ..., i++. 385 | public static void EmitIncrementFor(this ILGenerator il, LocalBuilder conditionGreater, Action emitBody) 386 | { 387 | Label loopBegin = il.DefineLabel(); 388 | Label condtionLabel = il.DefineLabel(); 389 | 390 | // var i = 0 391 | LocalBuilder forI = il.DeclareLocal(typeof(int)); 392 | il.EmitLdc_I4(0); 393 | il.EmitStloc(forI); 394 | il.Emit(OpCodes.Br, condtionLabel); 395 | 396 | il.MarkLabel(loopBegin); 397 | emitBody(forI); 398 | 399 | // i++ 400 | il.EmitLdloc(forI); 401 | il.EmitLdc_I4(1); 402 | il.Emit(OpCodes.Add); 403 | il.EmitStloc(forI); 404 | 405 | //// i < *** 406 | il.MarkLabel(condtionLabel); 407 | il.EmitLdloc(forI); 408 | il.EmitLdloc(conditionGreater); 409 | il.Emit(OpCodes.Blt, loopBegin); 410 | } 411 | } 412 | } -------------------------------------------------------------------------------- /src/MessagePack.FSharpExtensions/Internal/ReflectionExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Yoshifumi Kawai and contributors 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | using System; 22 | using System.Reflection; 23 | using System.Runtime.CompilerServices; 24 | 25 | namespace MessagePack.Internal 26 | { 27 | internal static class ReflectionExtensions 28 | { 29 | public static bool IsNullable(this System.Reflection.TypeInfo type) 30 | { 31 | return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Nullable<>); 32 | } 33 | 34 | public static bool IsPublic(this System.Reflection.TypeInfo type) 35 | { 36 | return type.IsPublic; 37 | } 38 | 39 | public static bool IsAnonymous(this System.Reflection.TypeInfo type) 40 | { 41 | return type.Namespace == null 42 | && type.IsSealed 43 | && (type.Name.StartsWith("<>f__AnonymousType", StringComparison.Ordinal) 44 | || type.Name.StartsWith("<>__AnonType", StringComparison.Ordinal) 45 | || type.Name.StartsWith("VB$AnonymousType_", StringComparison.Ordinal)) 46 | && type.IsDefined(typeof(CompilerGeneratedAttribute), false); 47 | } 48 | 49 | public static bool IsIndexer(this System.Reflection.PropertyInfo propertyInfo) 50 | { 51 | return propertyInfo.GetIndexParameters().Length > 0; 52 | } 53 | 54 | public static bool IsConstructedGenericType(this System.Reflection.TypeInfo type) 55 | { 56 | return type.AsType().IsConstructedGenericType; 57 | } 58 | 59 | public static MethodInfo GetGetMethod(this PropertyInfo propInfo) 60 | { 61 | return propInfo.GetMethod; 62 | } 63 | 64 | public static MethodInfo GetSetMethod(this PropertyInfo propInfo) 65 | { 66 | return propInfo.SetMethod; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/MessagePack.FSharpExtensions/MessagePack.FSharpExtensions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | netstandard2.0;netcoreapp3.1;net6.0 5 | $(DefineConstants);SPAN_BUILTIN 6 | 8.0 7 | pocketberserker 8 | pocketberserker 9 | README.md 10 | https://github.com/pocketberserker/MessagePack.FSharpExtensions 11 | MIT 12 | false 13 | MessagePack extensions for F# 14 | F#;fsharp;Msgpack;MessagePack;Serialization;Formatter;Serializer 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/MessagePack.FSharpExtensions/MonoProtection.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Yoshifumi Kawai and contributors 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | using System; 22 | using System.Threading; 23 | 24 | namespace MessagePack.FSharp 25 | { 26 | /// 27 | /// Special behavior for running on the mono runtime. 28 | /// 29 | internal struct MonoProtection 30 | { 31 | /// 32 | /// Gets a value indicating whether the mono runtime is executing this code. 33 | /// 34 | internal static bool IsRunningOnMono => Type.GetType("Mono.Runtime") != null; 35 | 36 | /// 37 | /// A lock that we enter on mono when generating dynamic types. 38 | /// 39 | private static readonly object RefEmitLock = new object(); 40 | 41 | /// 42 | /// The method to call within the expression of a using statement whose block surrounds all Ref.Emit code. 43 | /// 44 | /// The value to be disposed of to exit the Ref.Emit lock. 45 | /// 46 | /// This is a no-op except when running on Mono. 47 | /// Mono's implementation of Ref.Emit is not thread-safe so we have to lock around all use of it 48 | /// when using that runtime. 49 | /// 50 | internal static MonoProtectionDisposal EnterRefEmitLock() => IsRunningOnMono ? new MonoProtectionDisposal(RefEmitLock) : default; 51 | } 52 | 53 | internal struct MonoProtectionDisposal : IDisposable 54 | { 55 | private readonly object lockObject; 56 | 57 | internal MonoProtectionDisposal(object lockObject) 58 | { 59 | this.lockObject = lockObject; 60 | Monitor.Enter(lockObject); 61 | } 62 | 63 | public void Dispose() 64 | { 65 | if (this.lockObject is object) 66 | { 67 | Monitor.Exit(this.lockObject); 68 | } 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /tests/MessagePack.FSharpExtensions.Tests/AsyncTest.fs: -------------------------------------------------------------------------------- 1 | module MessagePack.Tests.AsyncTest 2 | 3 | open Xunit 4 | 5 | [] 6 | let ``async value`` () = 7 | 8 | let input = async.Return(1) 9 | let actual = convert input 10 | Assert.Equal(1, Async.RunSynchronously actual) 11 | -------------------------------------------------------------------------------- /tests/MessagePack.FSharpExtensions.Tests/ClassTest.fs: -------------------------------------------------------------------------------- 1 | module MessagePack.Tests.ClassTest 2 | 3 | open Xunit 4 | open System 5 | open MessagePack 6 | open MessagePack.Resolvers 7 | 8 | [] 9 | type SimpleClass(init: int) = 10 | [] 11 | let mutable inner = init 12 | 13 | new() = SimpleClass(0) 14 | 15 | [] 16 | member __.Property with set(value) = inner <- value and get() = inner 17 | 18 | let convert<'T>(value: 'T) = 19 | let options = MessagePackSerializerOptions.Standard.WithResolver(DynamicContractlessObjectResolverAllowPrivate.Instance) 20 | let bytes = MessagePackSerializer.Serialize(value, options) 21 | MessagePackSerializer.Deserialize<'T>(ReadOnlyMemory(bytes), options) 22 | 23 | [] 24 | let ``private member`` () = 25 | 26 | let input = SimpleClass(1) 27 | let actual = convert input 28 | Assert.Equal(input.Property, actual.Property) 29 | -------------------------------------------------------------------------------- /tests/MessagePack.FSharpExtensions.Tests/CollectionTest.fs: -------------------------------------------------------------------------------- 1 | module MessagePack.Tests.CollectionTest 2 | 3 | open Xunit 4 | 5 | [] 6 | let ``fsharp list`` () = 7 | 8 | let input: int list = [] 9 | let actual = convertEq input 10 | Assert.Equal(box input, box actual) 11 | 12 | let input = [1] 13 | let actual = convertEq input 14 | Assert.Equal(box input, box actual) 15 | 16 | [] 17 | let ``fsharp map`` () = 18 | 19 | let input= Map.empty 20 | let actual = convertEq input 21 | Assert.Equal(box input, box actual) 22 | 23 | let input = Map.empty |> Map.add 0 true 24 | let actual = convertEq input 25 | Assert.Equal(box input, box actual) 26 | 27 | [] 28 | let ``fsharp set`` () = 29 | 30 | let input = Seq.empty 31 | let actual = convert input 32 | Assert.Equal(box input, box actual) 33 | 34 | let input = Seq.singleton 1 35 | let actual = convertEq input 36 | Assert.Equal(box input, box actual) 37 | 38 | -------------------------------------------------------------------------------- /tests/MessagePack.FSharpExtensions.Tests/DUTest.fs: -------------------------------------------------------------------------------- 1 | module MessagePack.Tests.DUTest 2 | 3 | open Xunit 4 | open MessagePack 5 | 6 | [] 7 | type SimpleUnion = 8 | | A 9 | | B of int 10 | | C of int64 * float32 11 | 12 | [] 13 | let simple () = 14 | 15 | let input = A 16 | let actual = convert input 17 | Assert.Equal(input, actual) 18 | 19 | let input = B 100 20 | let actual = convert input 21 | Assert.Equal(input, actual) 22 | 23 | let input = C(99999999L, -123.43f) 24 | let actual = convert input 25 | Assert.Equal(input, actual) 26 | 27 | type StringKeyUnion = | D of Prop : int 28 | 29 | [] 30 | let ``string key`` () = 31 | 32 | let input = D 1 33 | let actual = convert input 34 | Assert.Equal(input, actual) 35 | 36 | let mutable beforeCallback = false 37 | let mutable afterCallback = false 38 | 39 | [] 40 | type CallbackUnion = 41 | | Call 42 | with 43 | interface IMessagePackSerializationCallbackReceiver with 44 | override this.OnBeforeSerialize() = 45 | beforeCallback <- true 46 | override this.OnAfterDeserialize() = 47 | afterCallback <- true 48 | 49 | [] 50 | let ``receive callback`` () = 51 | let input = Call 52 | convertEq input |> ignore 53 | Assert.True(beforeCallback) 54 | Assert.True(afterCallback) 55 | 56 | module Compatibility = 57 | 58 | [)>] 59 | [)>] 60 | [)>] 61 | type CsSimpleUnion = interface end 62 | 63 | and []CsA() = 64 | interface CsSimpleUnion 65 | 66 | and []CsB() = 67 | 68 | [] 69 | member val Item: int = 0 with get, set 70 | 71 | interface CsSimpleUnion 72 | 73 | and []CsC() = 74 | 75 | [] 76 | member val Item1: int64 = 0L with get, set 77 | 78 | [] 79 | member val Item2: float32 = 0.0f with get, set 80 | 81 | interface CsSimpleUnion 82 | 83 | [] 84 | let simple () = 85 | 86 | let input = A 87 | let actual = convert input |> box 88 | Assert.True(actual :? CsA) 89 | 90 | let input = B 100 91 | match convert input |> box with 92 | | :? CsB as actual -> 93 | Assert.Equal(100, actual.Item) 94 | | actual -> Assert.True(false, sprintf "expected: CsB, but was: %A" actual) 95 | 96 | let input = C(99999999L, -123.43f) 97 | match convert input |> box with 98 | | :? CsC as actual -> 99 | Assert.Equal(99999999L, actual.Item1) 100 | Assert.Equal(-123.43f, actual.Item2) 101 | | actual -> Assert.True(false, sprintf "expected: CsC, but was: %A" actual) 102 | 103 | [)>] 104 | type CsStringKeyUnion = interface end 105 | 106 | and []CsD() = 107 | 108 | [] 109 | member val Prop: int = 0 with get, set 110 | 111 | interface CsStringKeyUnion 112 | 113 | [] 114 | let ``string key`` () = 115 | 116 | let input = D 100 117 | match convert input |> box with 118 | | :? CsD as actual -> 119 | Assert.Equal(100, actual.Prop) 120 | | actual -> Assert.True(false, sprintf "expected: CsD, but was: %A" actual) 121 | -------------------------------------------------------------------------------- /tests/MessagePack.FSharpExtensions.Tests/Helper.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module MessagePack.Tests.Helper 3 | 4 | open System 5 | open MessagePack 6 | open MessagePack.Resolvers 7 | open MessagePack.FSharp 8 | 9 | type WithFSharpDefaultResolver() = 10 | interface IFormatterResolver with 11 | member __.GetFormatter<'T>() = 12 | match FSharpResolver.Instance.GetFormatter<'T>() with 13 | | null -> StandardResolver.Instance.GetFormatter<'T>() 14 | | x -> x 15 | 16 | let convert<'T, 'U>(value: 'T) = 17 | let resolver = WithFSharpDefaultResolver() :> IFormatterResolver 18 | let options = MessagePackSerializerOptions.Standard.WithResolver(resolver) 19 | let bytes = MessagePackSerializer.Serialize(value, options) 20 | MessagePackSerializer.Deserialize<'U>(ReadOnlyMemory(bytes), options) 21 | 22 | let convertEq (value: 'T) = convert<'T, 'T> value 23 | -------------------------------------------------------------------------------- /tests/MessagePack.FSharpExtensions.Tests/MessagePack.FSharpExtensions.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/MessagePack.FSharpExtensions.Tests/OptionTest.fs: -------------------------------------------------------------------------------- 1 | module MessagePack.Tests.OptionTest 2 | 3 | open Xunit 4 | 5 | [] 6 | let ``option some`` () = 7 | 8 | let input = Some 1 9 | let actual = convert input 10 | Assert.Equal(input, actual) 11 | 12 | [] 13 | let ``option none`` () = 14 | 15 | let input: int option = None 16 | let actual = convert input 17 | Assert.Equal(input, actual) 18 | 19 | [] 20 | let ``voption some`` () = 21 | 22 | let input = ValueSome 1 23 | let actual = convert input 24 | Assert.Equal(input, actual) 25 | 26 | [] 27 | let ``voption none`` () = 28 | 29 | let input: int voption = ValueNone 30 | let actual = convert input 31 | Assert.Equal(input, actual) 32 | -------------------------------------------------------------------------------- /tests/MessagePack.FSharpExtensions.Tests/RecordTest.fs: -------------------------------------------------------------------------------- 1 | module MessagePack.Tests.RecordTest 2 | 3 | open Xunit 4 | open MessagePack 5 | 6 | [] 7 | type SimpleRecord = { 8 | [] 9 | Property1: int 10 | [] 11 | Property2: int64 12 | [] 13 | Property3: float32 14 | } 15 | 16 | [] 17 | let ``simple record`` () = 18 | 19 | let input = { Property1 = 100; Property2 = 99999999L; Property3 = -123.43f } 20 | let actual = convert input 21 | Assert.Equal(input, actual) 22 | 23 | [] 24 | type SimpleStringKeyRecord = { 25 | Prop1: int 26 | Prop2: int64 27 | Prop3: float32 28 | } 29 | 30 | [] 31 | let ``string key`` () = 32 | 33 | let input = { Prop1 = 100; Prop2 = 99999999L; Prop3 = -123.43f } 34 | let actual = convert input 35 | Assert.Equal(input, actual) 36 | 37 | [] 38 | type StructRecord = { 39 | [] 40 | X: int 41 | [] 42 | Y: int 43 | } 44 | 45 | [] 46 | let ``struct record`` () = 47 | 48 | let input = { X = 1; Y = 2 } 49 | let actual = convert input 50 | Assert.Equal(input, actual) 51 | 52 | [] 53 | let ``anonymous record`` () = 54 | 55 | let input = {| Foo = 1; Bar = 2 |} 56 | let actual = convert input 57 | Assert.Equal(input, actual) 58 | -------------------------------------------------------------------------------- /tests/MessagePack.FSharpExtensions.Tests/StructDUTest.fs: -------------------------------------------------------------------------------- 1 | module MessagePack.Tests.StructDUTest 2 | 3 | open Xunit 4 | open MessagePack 5 | 6 | [] 7 | type StructUnion = 8 | | E 9 | | F of Prop1 : int 10 | | G of Prop2 : int64 * Prop3: float32 11 | 12 | [] 13 | let ``struct `` () = 14 | 15 | let input = E 16 | let actual = convert input 17 | Assert.Equal(input, actual) 18 | 19 | let input = F 100 20 | let actual = convert input 21 | Assert.Equal(input, actual) 22 | 23 | let input = G(99999999L, -123.43f) 24 | let actual = convert input 25 | Assert.Equal(input, actual) 26 | 27 | [] 28 | let ``builtin(string key)`` () = 29 | 30 | let input: Result = Ok 1 31 | let actual = convert input 32 | Assert.Equal(input, actual) 33 | 34 | let input: Result = Error "error" 35 | let actual = convert input 36 | Assert.Equal(input, actual) 37 | 38 | let mutable beforeStructCallback = false 39 | let mutable afterStructCallback = false 40 | 41 | [] 42 | type StructCallbackUnion = 43 | | StructCall 44 | with 45 | interface IMessagePackSerializationCallbackReceiver with 46 | override this.OnBeforeSerialize() = 47 | beforeStructCallback <- true 48 | override this.OnAfterDeserialize() = 49 | afterStructCallback <- true 50 | 51 | [] 52 | let ``receive callback struct union`` () = 53 | let input = StructCall 54 | convertEq input |> ignore 55 | Assert.True(beforeStructCallback) 56 | Assert.True(afterStructCallback) 57 | 58 | module Compatibility = 59 | 60 | [)>] 61 | [)>] 62 | [)>] 63 | type CsStructUnion = interface end 64 | 65 | and []CsE = 66 | interface CsStructUnion 67 | 68 | and []CsF(item: int) = 69 | 70 | [] 71 | member __.Item = item 72 | 73 | interface CsStructUnion 74 | 75 | and []CsG(item1: int64, item2: float32) = 76 | 77 | [] 78 | member __.Item1 = item1 79 | 80 | [] 81 | member __.Item2 = item2 82 | 83 | interface CsStructUnion 84 | 85 | [] 86 | let ``struct `` () = 87 | 88 | let input = E 89 | let actual = convert input |> box 90 | Assert.True(actual :? CsE) 91 | 92 | let input = F 100 93 | match convert input |> box with 94 | | :? CsF as actual -> 95 | Assert.Equal(100, actual.Item) 96 | | actual -> Assert.True(false, sprintf "expected: CsF, but was: %A" actual) 97 | 98 | let input = G(99999999L, -123.43f) 99 | match convert input |> box with 100 | | :? CsG as actual -> 101 | Assert.Equal(99999999L, actual.Item1) 102 | Assert.Equal(-123.43f, actual.Item2) 103 | | actual -> Assert.True(false, sprintf "expected: CsG, but was: %A" actual) 104 | 105 | [)>] 106 | [)>] 107 | type CsResult<'T, 'U> = interface end 108 | 109 | and []CsOk(resultValue: int) = 110 | 111 | member __.ResultValue = resultValue 112 | 113 | interface CsResult 114 | 115 | and []CsError(errorValue: string) = 116 | 117 | member __.ErrorValue = errorValue 118 | 119 | interface CsResult 120 | 121 | [] 122 | let ``builtin(string key)`` () = 123 | 124 | let input: Result = Ok 1 125 | match convert, CsResult> input |> box with 126 | | :? CsOk as actual -> 127 | Assert.Equal(1, actual.ResultValue) 128 | | actual -> Assert.True(false, sprintf "expected: CsOk, but was: %A" actual) 129 | 130 | let input: Result = Error "error" 131 | match convert, CsResult> input |> box with 132 | | :? CsError as actual -> 133 | Assert.Equal("error", actual.ErrorValue) 134 | | actual -> Assert.True(false, sprintf "expected: CsError, but was: %A" actual) 135 | -------------------------------------------------------------------------------- /tests/MessagePack.FSharpExtensions.Tests/UnitTest.fs: -------------------------------------------------------------------------------- 1 | module MessagePack.Tests.UnitTest 2 | 3 | open Xunit 4 | 5 | [] 6 | let ``unit value`` () = 7 | 8 | let input = () 9 | let actual = convert input 10 | Assert.Equal(input, actual) 11 | --------------------------------------------------------------------------------