diff --git a/Documentation/known-issues.md b/Documentation/known-issues.md index a5f8bb038..735164987 100644 --- a/Documentation/known-issues.md +++ b/Documentation/known-issues.md @@ -1,23 +1,6 @@ Known issues & workarounds ========================== -## El Capitan support -If you try to use the `dotnet` commands on El Capitan (OS X 10.11), you will encounter errors as it is not currently -fully supported for all scenarios. - -**Issues tracking this:** - -* [#498](https://github.com/dotnet/cli/issues/498) -* [#291](https://github.com/dotnet/cli/issues/291) - -**Affects:** most of the commands, but more than likely you will not be able to -use `dotnet compile` and `dotnet-run` on El Capitan. For others, there is a -workaround. - -**Workaround:** use the --runtime switch with the value of `osx.10.11-x64` in -`dotnet restore` and `dotnet publish` and you will be able to run your app from -the published directory. - ## Resolving the Standard library packages The StdLib package is on a MyGet feed. In order to restore it, a MyGet feed needs to be added to the NuGet feeds, either locally per application or in a central location. diff --git a/Documentation/specs/runtime-configuration-file.md b/Documentation/specs/runtime-configuration-file.md index f35de1ec7..ff2df4c33 100644 --- a/Documentation/specs/runtime-configuration-file.md +++ b/Documentation/specs/runtime-configuration-file.md @@ -29,7 +29,8 @@ The files are both JSON files stored in UTF-8 encoding. Below are sample files. "gcConcurrent": false, "framework": { "name": "Microsoft.DotNetCore", - "version": "1.0.1" + "version": "1.0.1", + "rollForward": false, } } } @@ -118,7 +119,11 @@ This section is copied verbatim from an identical section in the input `project. * `gcServer` - Boolean indicating if the server GC should be used (Default: _TBD_). Note: This is designed to mirror the existing [app.config](https://msdn.microsoft.com/en-us/library/ms229357.aspx) setting) * `gcConcurrent` - Boolean indicating if background garbage collection should be used (Default: _TBD_). Note: This is designed to mirror the existing [app.config](https://msdn.microsoft.com/en-us/library/yhwwzef8.aspx) setting). -* `framework` - Indicates the name and version of the shared framework to use when activating the application. The presence of this section indicates that the application is a portable app designed to use a shared redistributable framework. +* `framework` - Indicates the `name`, `version`, and other properties of the shared framework to use when activating the application. The presence of this section indicates that the application is a portable app designed to use a shared redistributable framework. + * `rollForward` - When `false`, the framework version is strictly obeyed by the host. When `rollForward` is unspecified or specified as `true`, the framework from either the same or a higher version that differs only in the `SemVer` patch field will be used. + * For example, if `version=1.0.1` and `rollForward` is `true`, the host would load the shared framework from `1.0.{n}`, where `n >= 1`, but will not load from `1.1.0`, even if present. When `rollForward` is `false`, the shared framework will be loaded from `1.0.1` strictly. + * **Note:** This does not apply to `SemVer`'s `prerelease` versions, but only for `production` releases. + * **Note:** This section will not be present for standalone applications that do not rely upon a shared framework. * Others _TBD_ These settings are read by `corehost` to determine how to initialize the runtime. All versions of `corehost` **must ignore** settings in this section that they do not understand (thus allowing new settings to be added in later versions). diff --git a/TestAssets/FSharpTestProjects/CompileFail/project.json b/TestAssets/FSharpTestProjects/CompileFail/project.json index 40621376b..84d87f6df 100644 --- a/TestAssets/FSharpTestProjects/CompileFail/project.json +++ b/TestAssets/FSharpTestProjects/CompileFail/project.json @@ -1,20 +1,19 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - "compilerName": "fsc", - "compileFiles": [ - "Program.fs" - ], - "dependencies": { - "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-151221", - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "compilerName": "fsc", + "compileFiles": [ + "Program.fs" + ], + "dependencies": { + "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-151221", + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/FSharpTestProjects/TestApp/FSharpTestApp.xproj b/TestAssets/FSharpTestProjects/TestApp/FSharpTestApp.xproj index 4b792e869..e48ab0105 100644 --- a/TestAssets/FSharpTestProjects/TestApp/FSharpTestApp.xproj +++ b/TestAssets/FSharpTestProjects/TestApp/FSharpTestApp.xproj @@ -5,7 +5,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + a666217d-2aca-4866-b109-ea476e51c7aa FSharpTestApp @@ -16,5 +16,5 @@ 2.0 - + diff --git a/TestAssets/FSharpTestProjects/TestApp/project.json b/TestAssets/FSharpTestProjects/TestApp/project.json index 141270ca8..90fcb92b9 100644 --- a/TestAssets/FSharpTestProjects/TestApp/project.json +++ b/TestAssets/FSharpTestProjects/TestApp/project.json @@ -1,21 +1,20 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - "compilerName": "fsc", - "compileFiles": [ - "Program.fs" - ], - "dependencies": { - "TestLibrary": "1.0.0-*", - "NETStandard.Library": "1.0.0-rc2-23901", - "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-151221" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "compilerName": "fsc", + "compileFiles": [ + "Program.fs" + ], + "dependencies": { + "TestLibrary": "1.0.0-*", + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-151221" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/FSharpTestProjects/TestAppWithArgs/project.json b/TestAssets/FSharpTestProjects/TestAppWithArgs/project.json index 40621376b..84d87f6df 100644 --- a/TestAssets/FSharpTestProjects/TestAppWithArgs/project.json +++ b/TestAssets/FSharpTestProjects/TestAppWithArgs/project.json @@ -1,20 +1,19 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - "compilerName": "fsc", - "compileFiles": [ - "Program.fs" - ], - "dependencies": { - "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-151221", - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "compilerName": "fsc", + "compileFiles": [ + "Program.fs" + ], + "dependencies": { + "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-151221", + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/FSharpTestProjects/TestLibrary/FSharpTestLibrary.xproj b/TestAssets/FSharpTestProjects/TestLibrary/FSharpTestLibrary.xproj index ae10bbff6..de36bbed6 100644 --- a/TestAssets/FSharpTestProjects/TestLibrary/FSharpTestLibrary.xproj +++ b/TestAssets/FSharpTestProjects/TestLibrary/FSharpTestLibrary.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + ec801982-096b-4af3-a42b-7881b1a7380e FSharpTestLibrary @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/TestAssets/FSharpTestProjects/TestLibrary/project.json b/TestAssets/FSharpTestProjects/TestLibrary/project.json index 61961870b..0b29b1792 100644 --- a/TestAssets/FSharpTestProjects/TestLibrary/project.json +++ b/TestAssets/FSharpTestProjects/TestLibrary/project.json @@ -1,17 +1,17 @@ { - "version": "1.0.0-*", - "dependencies": { - "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-151221", - "NETStandard.Library": "1.0.0-rc2-23901" - }, - "compilerName": "fsc", - "compileFiles": [ - "Helper2.fs", - "Helper.fs" - ], - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "dependencies": { + "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-151221", + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "compilerName": "fsc", + "compileFiles": [ + "Helper2.fs", + "Helper.fs" + ], + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/ProjectModelServer/DthTestProjects/src/BrokenProjectPathSample/project.json b/TestAssets/ProjectModelServer/DthTestProjects/src/BrokenProjectPathSample/project.json index d183d244f..7ce8666ac 100644 --- a/TestAssets/ProjectModelServer/DthTestProjects/src/BrokenProjectPathSample/project.json +++ b/TestAssets/ProjectModelServer/DthTestProjects/src/BrokenProjectPathSample/project.json @@ -1,11 +1,11 @@ { - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "EmptyLibrary": "1.0.0-*" - }, - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "EmptyLibrary": "1.0.0-*" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/ProjectModelServer/DthTestProjects/src/EmptyConsoleApp/project.json b/TestAssets/ProjectModelServer/DthTestProjects/src/EmptyConsoleApp/project.json index 38a3ba296..a81519a70 100644 --- a/TestAssets/ProjectModelServer/DthTestProjects/src/EmptyConsoleApp/project.json +++ b/TestAssets/ProjectModelServer/DthTestProjects/src/EmptyConsoleApp/project.json @@ -1,12 +1,12 @@ { - "dependencies": { }, + "dependencies": {}, "frameworks": { "netstandardapp1.5": { "imports": "dnxcore50", "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" + "NETStandard.Library": "1.5.0-rc2-23911" } }, - "dnx451": { } + "dnx451": {} } } diff --git a/TestAssets/ProjectModelServer/DthTestProjects/src/EmptyLibrary/project.json b/TestAssets/ProjectModelServer/DthTestProjects/src/EmptyLibrary/project.json index 667685804..17c4e9e46 100644 --- a/TestAssets/ProjectModelServer/DthTestProjects/src/EmptyLibrary/project.json +++ b/TestAssets/ProjectModelServer/DthTestProjects/src/EmptyLibrary/project.json @@ -1,11 +1,11 @@ { "version": "1.0.0-*", - "dependencies": { }, + "dependencies": {}, "frameworks": { "netstandard1.3": { "imports": "dnxcore50", "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" + "NETStandard.Library": "1.5.0-rc2-23911" } } } diff --git a/TestAssets/ProjectModelServer/DthTestProjects/src/FailReleaseProject/project.json b/TestAssets/ProjectModelServer/DthTestProjects/src/FailReleaseProject/project.json index 505047ad2..83b5d4335 100644 --- a/TestAssets/ProjectModelServer/DthTestProjects/src/FailReleaseProject/project.json +++ b/TestAssets/ProjectModelServer/DthTestProjects/src/FailReleaseProject/project.json @@ -3,9 +3,9 @@ "netstandardapp1.5": { "imports": "dnxcore50", "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" + "NETStandard.Library": "1.5.0-rc2-23911" } } }, - "dependencies": { } + "dependencies": {} } diff --git a/TestAssets/ProjectModelServer/DthTestProjects/src/IncompatiblePackageSample/project.json b/TestAssets/ProjectModelServer/DthTestProjects/src/IncompatiblePackageSample/project.json index 58af503dc..37a82e632 100644 --- a/TestAssets/ProjectModelServer/DthTestProjects/src/IncompatiblePackageSample/project.json +++ b/TestAssets/ProjectModelServer/DthTestProjects/src/IncompatiblePackageSample/project.json @@ -1,11 +1,11 @@ { - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "Microsoft.Web.Administration": "7.0.0" - }, - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.Web.Administration": "7.0.0" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/ProjectModelServer/IncorrectGlobalJson/src/Project1/project.json b/TestAssets/ProjectModelServer/IncorrectGlobalJson/src/Project1/project.json index 8c87d36c9..bb3a9cebf 100755 --- a/TestAssets/ProjectModelServer/IncorrectGlobalJson/src/Project1/project.json +++ b/TestAssets/ProjectModelServer/IncorrectGlobalJson/src/Project1/project.json @@ -1,14 +1,12 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23811" - }, - - "frameworks": { - "dnxcore50": { } - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "dnxcore50": {} + } } diff --git a/TestAssets/TestPackages/dotnet-hello/v1/dotnet-hello/dotnet-hello.xproj b/TestAssets/TestPackages/dotnet-hello/v1/dotnet-hello/dotnet-hello.xproj index 635819547..d49a161f4 100644 --- a/TestAssets/TestPackages/dotnet-hello/v1/dotnet-hello/dotnet-hello.xproj +++ b/TestAssets/TestPackages/dotnet-hello/v1/dotnet-hello/dotnet-hello.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + b8055234-9a66-4ba0-8b4c-d5e431494fe3 dotnet-hello @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/TestAssets/TestPackages/dotnet-hello/v1/dotnet-hello/project.json b/TestAssets/TestPackages/dotnet-hello/v1/dotnet-hello/project.json index ea118a24d..cafaf976b 100644 --- a/TestAssets/TestPackages/dotnet-hello/v1/dotnet-hello/project.json +++ b/TestAssets/TestPackages/dotnet-hello/v1/dotnet-hello/project.json @@ -1,16 +1,14 @@ { - "version": "1.0.0", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestPackages/dotnet-hello/v2/dotnet-hello/dotnet-hello.xproj b/TestAssets/TestPackages/dotnet-hello/v2/dotnet-hello/dotnet-hello.xproj index 7011c102d..7c2da659a 100644 --- a/TestAssets/TestPackages/dotnet-hello/v2/dotnet-hello/dotnet-hello.xproj +++ b/TestAssets/TestPackages/dotnet-hello/v2/dotnet-hello/dotnet-hello.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 282c5014-d0cd-4dde-af4e-531e4a2e7bcd dotnet-hello @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/TestAssets/TestPackages/dotnet-hello/v2/dotnet-hello/project.json b/TestAssets/TestPackages/dotnet-hello/v2/dotnet-hello/project.json index 464f94681..c1bd9c205 100644 --- a/TestAssets/TestPackages/dotnet-hello/v2/dotnet-hello/project.json +++ b/TestAssets/TestPackages/dotnet-hello/v2/dotnet-hello/project.json @@ -1,16 +1,14 @@ { - "version": "2.0.0", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "2.0.0", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/AppWithDirectAndToolDependency/project.json b/TestAssets/TestProjects/AppWithDirectAndToolDependency/project.json index 679acc364..7bcb08e63 100644 --- a/TestAssets/TestProjects/AppWithDirectAndToolDependency/project.json +++ b/TestAssets/TestProjects/AppWithDirectAndToolDependency/project.json @@ -1,23 +1,25 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "dotnet-hello": { "version": "1.0.0", "target": "package" } - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } - }, - - "testRunner": "must-be-specified-to-generate-deps", - - "tools": { - "dotnet-hello": { "version": "2.0.0", "target": "package" } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "dotnet-hello": { + "version": "1.0.0", + "target": "package" } + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" + } + }, + "testRunner": "must-be-specified-to-generate-deps", + "tools": { + "dotnet-hello": { + "version": "2.0.0", + "target": "package" + } + } } diff --git a/TestAssets/TestProjects/AppWithDirectDependency/project.json b/TestAssets/TestProjects/AppWithDirectDependency/project.json index 2ce5cf482..14cc1022a 100644 --- a/TestAssets/TestProjects/AppWithDirectDependency/project.json +++ b/TestAssets/TestProjects/AppWithDirectDependency/project.json @@ -1,19 +1,19 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "testRunner": "must-be-specified-to-generate-deps", - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "dotnet-hello": {"version": "1.0.0", "target": "package"} - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "testRunner": "must-be-specified-to-generate-deps", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "dotnet-hello": { + "version": "1.0.0", + "target": "package" } + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" + } + } } diff --git a/TestAssets/TestProjects/AppWithToolDependency/project.json b/TestAssets/TestProjects/AppWithToolDependency/project.json index 9537feb8f..4628c7718 100644 --- a/TestAssets/TestProjects/AppWithToolDependency/project.json +++ b/TestAssets/TestProjects/AppWithToolDependency/project.json @@ -1,20 +1,20 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } - }, - - "tools": { - "dotnet-hello": { "version": "2.0.0", "target": "package" } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + }, + "tools": { + "dotnet-hello": { + "version": "2.0.0", + "target": "package" + } + } } diff --git a/TestAssets/TestProjects/CompileFail/project.json b/TestAssets/TestProjects/CompileFail/project.json index c02d0dffe..2e5ebbe30 100644 --- a/TestAssets/TestProjects/CompileFail/project.json +++ b/TestAssets/TestProjects/CompileFail/project.json @@ -1,16 +1,14 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/DependencyContextValidator.xproj b/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/DependencyContextValidator.xproj index c827617ac..8a2f395fd 100644 --- a/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/DependencyContextValidator.xproj +++ b/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/DependencyContextValidator.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 6d84ef36-a5d5-4eaf-b38b-ced635473785 DependencyContextValidator @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/project.json b/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/project.json index 90a5334e8..00cd67349 100644 --- a/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/project.json +++ b/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/project.json @@ -1,16 +1,15 @@ { - "version": "1.0.0-*", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "Microsoft.Extensions.DependencyModel": { - "target": "project", - "version": "1.0.0-*" - } - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.Extensions.DependencyModel": { + "target": "project", + "version": "1.0.0-*" } + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" + } + } } diff --git a/TestAssets/TestProjects/DependencyContextValidator/TestApp/project.json b/TestAssets/TestProjects/DependencyContextValidator/TestApp/project.json index 4c2908690..965a3f06e 100644 --- a/TestAssets/TestProjects/DependencyContextValidator/TestApp/project.json +++ b/TestAssets/TestProjects/DependencyContextValidator/TestApp/project.json @@ -1,18 +1,16 @@ -{ - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true, - "preserveCompilationContext": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "DependencyContextValidator": "1.0.0-*" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } +{ + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true, + "preserveCompilationContext": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "DependencyContextValidator": "1.0.0-*" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/DependencyContextValidator/TestAppDeps/project.json b/TestAssets/TestProjects/DependencyContextValidator/TestAppDeps/project.json index 9f1f402f6..f41c7127f 100644 --- a/TestAssets/TestProjects/DependencyContextValidator/TestAppDeps/project.json +++ b/TestAssets/TestProjects/DependencyContextValidator/TestAppDeps/project.json @@ -1,17 +1,15 @@ -{ - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "DependencyContextValidator": "1.0.0-*" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } +{ + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "DependencyContextValidator": "1.0.0-*" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/OutputStandardOutputAndError/OutputStandardOutputAndError.xproj b/TestAssets/TestProjects/OutputStandardOutputAndError/OutputStandardOutputAndError.xproj index ee6ccb69d..0443b706a 100644 --- a/TestAssets/TestProjects/OutputStandardOutputAndError/OutputStandardOutputAndError.xproj +++ b/TestAssets/TestProjects/OutputStandardOutputAndError/OutputStandardOutputAndError.xproj @@ -5,7 +5,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 58808bbc-371e-47d6-a3d0-4902145eda4e OutputStandardOutputAndError @@ -16,5 +16,5 @@ 2.0 - + diff --git a/TestAssets/TestProjects/OutputStandardOutputAndError/project.json b/TestAssets/TestProjects/OutputStandardOutputAndError/project.json index c02d0dffe..2e5ebbe30 100644 --- a/TestAssets/TestProjects/OutputStandardOutputAndError/project.json +++ b/TestAssets/TestProjects/OutputStandardOutputAndError/project.json @@ -1,16 +1,14 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/PortableTests/PortableApp/project.json b/TestAssets/TestProjects/PortableTests/PortableApp/project.json index 4386a64ac..3111191ad 100644 --- a/TestAssets/TestProjects/PortableTests/PortableApp/project.json +++ b/TestAssets/TestProjects/PortableTests/PortableApp/project.json @@ -1,18 +1,17 @@ { - "compilationOptions": { - "emitEntryPoint": true - }, - "dependencies": { - }, - "frameworks": { - "netstandard1.5": { - "imports": [ - "dnxcore50", - "portable-net45+win8" - ], - "dependencies": { - "Microsoft.NETCore.App": "1.0.0-rc2-23911" - } - } + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": {}, + "frameworks": { + "netstandard1.5": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ], + "dependencies": { + "Microsoft.NETCore.App": "1.0.0-rc2-23911" + } } + } } diff --git a/TestAssets/TestProjects/PortableTests/StandaloneApp/project.json b/TestAssets/TestProjects/PortableTests/StandaloneApp/project.json index eaae7c1cd..2cf04776c 100644 --- a/TestAssets/TestProjects/PortableTests/StandaloneApp/project.json +++ b/TestAssets/TestProjects/PortableTests/StandaloneApp/project.json @@ -1,26 +1,26 @@ { - "compilationOptions": { - "emitEntryPoint": true - }, - "frameworks": { - "netstandardapp1.5": { - "imports": [ - "dnxcore50", - "portable-net45+win8" - ], - "dependencies": { - "Microsoft.NETCore.App": "1.0.0-rc2-23911" - } - } - }, - "runtimes": { - "win7-x64": {}, - "win7-x86": {}, - "osx.10.10-x64": {}, - "osx.10.11-x64": {}, - "ubuntu.14.04-x64": {}, - "centos.7-x64": {}, - "rhel.7.2-x64": {}, - "debian.8.2-x64": {} + "compilationOptions": { + "emitEntryPoint": true + }, + "frameworks": { + "netstandardapp1.5": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ], + "dependencies": { + "Microsoft.NETCore.App": "1.0.0-rc2-23911" + } } + }, + "runtimes": { + "win7-x64": { }, + "win7-x86": { }, + "osx.10.10-x64": { }, + "osx.10.11-x64": { }, + "ubuntu.14.04-x64": { }, + "centos.7-x64": { }, + "rhel.7.2-x64": { }, + "debian.8.2-x64": { } + } } diff --git a/TestAssets/TestProjects/RunTestsApps/TestAppMultiTarget/project.json b/TestAssets/TestProjects/RunTestsApps/TestAppMultiTarget/project.json index 5ed30dfa4..a081d6a7d 100644 --- a/TestAssets/TestProjects/RunTestsApps/TestAppMultiTarget/project.json +++ b/TestAssets/TestProjects/RunTestsApps/TestAppMultiTarget/project.json @@ -1,16 +1,15 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + } }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - } - }, - "net451": { } - } + "net451": {} + } } diff --git a/TestAssets/TestProjects/TestAppCompilationContext/TestApp/project.json b/TestAssets/TestProjects/TestAppCompilationContext/TestApp/project.json index ded1b2439..6df0b0c6d 100644 --- a/TestAssets/TestProjects/TestAppCompilationContext/TestApp/project.json +++ b/TestAssets/TestProjects/TestAppCompilationContext/TestApp/project.json @@ -1,19 +1,16 @@ -{ - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true, - "preserveCompilationContext": true - }, - - "dependencies": { - "TestLibrary": "1.0.0-*", - - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } +{ + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true, + "preserveCompilationContext": true + }, + "dependencies": { + "TestLibrary": "1.0.0-*", + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestAppCompilationContext/TestLibrary/TestLibrary.xproj b/TestAssets/TestProjects/TestAppCompilationContext/TestLibrary/TestLibrary.xproj index eb9f8bc2d..ae7910d0d 100644 --- a/TestAssets/TestProjects/TestAppCompilationContext/TestLibrary/TestLibrary.xproj +++ b/TestAssets/TestProjects/TestAppCompilationContext/TestLibrary/TestLibrary.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 947dd232-8d9b-4b78-9c6a-94f807d2dd58 TestLibrary @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/TestAssets/TestProjects/TestAppCompilationContext/TestLibrary/project.json b/TestAssets/TestProjects/TestAppCompilationContext/TestLibrary/project.json index dce6a0dad..f0b60ee88 100644 --- a/TestAssets/TestProjects/TestAppCompilationContext/TestLibrary/project.json +++ b/TestAssets/TestProjects/TestAppCompilationContext/TestLibrary/project.json @@ -1,17 +1,20 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "nowarn": [ "CS1591" ], - "xmlDoc": true, - "additionalArguments": [ "-highentropyva+" ] - }, - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "nowarn": [ + "CS1591" + ], + "xmlDoc": true, + "additionalArguments": [ + "-highentropyva+" + ] + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestAppWithArgs/TestAppWithArgs.xproj b/TestAssets/TestProjects/TestAppWithArgs/TestAppWithArgs.xproj index fbd214cbd..70d47bf5c 100644 --- a/TestAssets/TestProjects/TestAppWithArgs/TestAppWithArgs.xproj +++ b/TestAssets/TestProjects/TestAppWithArgs/TestAppWithArgs.xproj @@ -4,7 +4,7 @@ 14.0.23107 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + da8e0e9e-a6d6-4583-864c-8f40465e3a48 TestAppWithArgs @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/TestAssets/TestProjects/TestAppWithArgs/project.json b/TestAssets/TestProjects/TestAppWithArgs/project.json index c02d0dffe..2e5ebbe30 100644 --- a/TestAssets/TestProjects/TestAppWithArgs/project.json +++ b/TestAssets/TestProjects/TestAppWithArgs/project.json @@ -1,16 +1,14 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestAppWithContentPackage/TestAppWithContentPackage.xproj b/TestAssets/TestProjects/TestAppWithContentPackage/TestAppWithContentPackage.xproj index 718199108..7cc51e6eb 100644 --- a/TestAssets/TestProjects/TestAppWithContentPackage/TestAppWithContentPackage.xproj +++ b/TestAssets/TestProjects/TestAppWithContentPackage/TestAppWithContentPackage.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + ea239e10-75e8-4305-966e-fec926a5aee6 TestAppWithContentPackage @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/TestAssets/TestProjects/TestAppWithContentPackage/project.json b/TestAssets/TestProjects/TestAppWithContentPackage/project.json index 43575128d..6e9d2a179 100644 --- a/TestAssets/TestProjects/TestAppWithContentPackage/project.json +++ b/TestAssets/TestProjects/TestAppWithContentPackage/project.json @@ -1,11 +1,10 @@ -{ +{ "version": "1.0.0-*", "compilationOptions": { "emitEntryPoint": true }, - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", + "NETStandard.Library": "1.5.0-rc2-23911", "SharedContentA": "1.0.0-*" }, "frameworks": { @@ -13,4 +12,4 @@ "imports": "dnxcore50" } } -} \ No newline at end of file +} diff --git a/TestAssets/TestProjects/TestAppWithContents/TestAppWithContents.xproj b/TestAssets/TestProjects/TestAppWithContents/TestAppWithContents.xproj index 00103e020..1f1eafbea 100644 --- a/TestAssets/TestProjects/TestAppWithContents/TestAppWithContents.xproj +++ b/TestAssets/TestProjects/TestAppWithContents/TestAppWithContents.xproj @@ -4,7 +4,7 @@ 14.0.23107 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 0138cb8f-4aa9-4029-a21e-c07c30f425ba TestAppWithContents @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/TestAssets/TestProjects/TestAppWithContents/project.json b/TestAssets/TestProjects/TestAppWithContents/project.json index c149fddc2..e5f81bb6f 100644 --- a/TestAssets/TestProjects/TestAppWithContents/project.json +++ b/TestAssets/TestProjects/TestAppWithContents/project.json @@ -1,18 +1,15 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "content": "testcontentfile.txt", - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "content": "testcontentfile.txt", + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestAppWithLibrary/TestApp/TestApp.xproj b/TestAssets/TestProjects/TestAppWithLibrary/TestApp/TestApp.xproj index 4cef17daa..75b98c80d 100644 --- a/TestAssets/TestProjects/TestAppWithLibrary/TestApp/TestApp.xproj +++ b/TestAssets/TestProjects/TestAppWithLibrary/TestApp/TestApp.xproj @@ -5,7 +5,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 58808bbc-371e-47d6-a3d0-4902145eda4e TestApp @@ -16,5 +16,5 @@ 2.0 - + diff --git a/TestAssets/TestProjects/TestAppWithLibrary/TestApp/project.json b/TestAssets/TestProjects/TestAppWithLibrary/TestApp/project.json index f9f77f358..88dd7cbee 100644 --- a/TestAssets/TestProjects/TestAppWithLibrary/TestApp/project.json +++ b/TestAssets/TestProjects/TestAppWithLibrary/TestApp/project.json @@ -1,19 +1,19 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true, - "preserveCompilationContext": true + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true, + "preserveCompilationContext": true + }, + "dependencies": { + "TestLibrary": { + "target": "project", + "version": "1.0.0-*" }, - - "dependencies": { - "TestLibrary": { "target":"project", "version":"1.0.0-*" }, - - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestAppWithLibrary/TestLibrary/TestLibrary.xproj b/TestAssets/TestProjects/TestAppWithLibrary/TestLibrary/TestLibrary.xproj index eb9f8bc2d..ae7910d0d 100644 --- a/TestAssets/TestProjects/TestAppWithLibrary/TestLibrary/TestLibrary.xproj +++ b/TestAssets/TestProjects/TestAppWithLibrary/TestLibrary/TestLibrary.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 947dd232-8d9b-4b78-9c6a-94f807d2dd58 TestLibrary @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/TestAssets/TestProjects/TestAppWithLibrary/TestLibrary/project.json b/TestAssets/TestProjects/TestAppWithLibrary/TestLibrary/project.json index 8e8b984b1..f0b60ee88 100644 --- a/TestAssets/TestProjects/TestAppWithLibrary/TestLibrary/project.json +++ b/TestAssets/TestProjects/TestAppWithLibrary/TestLibrary/project.json @@ -1,17 +1,20 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "nowarn": [ "CS1591" ], - "xmlDoc": true, - "additionalArguments": [ "-highentropyva+" ] - }, - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23811" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "nowarn": [ + "CS1591" + ], + "xmlDoc": true, + "additionalArguments": [ + "-highentropyva+" + ] + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestAppWithScripts/TestApp.xproj b/TestAssets/TestProjects/TestAppWithScripts/TestApp.xproj index 4cef17daa..75b98c80d 100644 --- a/TestAssets/TestProjects/TestAppWithScripts/TestApp.xproj +++ b/TestAssets/TestProjects/TestAppWithScripts/TestApp.xproj @@ -5,7 +5,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 58808bbc-371e-47d6-a3d0-4902145eda4e TestApp @@ -16,5 +16,5 @@ 2.0 - + diff --git a/TestAssets/TestProjects/TestAppWithScripts/project.json b/TestAssets/TestProjects/TestAppWithScripts/project.json index 8de4803d7..11692199c 100644 --- a/TestAssets/TestProjects/TestAppWithScripts/project.json +++ b/TestAssets/TestProjects/TestAppWithScripts/project.json @@ -1,24 +1,21 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } - }, - - "scripts": { - "prepublish" : ["echoscript prepublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:TargetFramework%? ?%publish:Runtime%?"], - "postpublish" : ["echoscript postpublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:TargetFramework%? ?%publish:Runtime%?"], - - "precompile" : ["echoscript precompile_output ?%compile:ProjectPath%? ?%compile:Configuration%? ?%compile:OutputPath%? ?%compile:TargetFramework%? ?%compile:Runtime%?"], - "postcompile" : ["echoscript postcompile_output ?%compile:ProjectPath%? ?%compile:Configuration%? ?%compile:OutputPath%? ?%compile:TargetFramework%? ?%compile:Runtime%?"] + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + }, + "scripts": { + "prepublish": [ "echoscript prepublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:TargetFramework%? ?%publish:Runtime%?" ], + "postpublish": [ "echoscript postpublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:TargetFramework%? ?%publish:Runtime%?" ], + + "precompile": [ "echoscript precompile_output ?%compile:ProjectPath%? ?%compile:Configuration%? ?%compile:OutputPath%? ?%compile:TargetFramework%? ?%compile:Runtime%?" ], + "postcompile": [ "echoscript postcompile_output ?%compile:ProjectPath%? ?%compile:Configuration%? ?%compile:OutputPath%? ?%compile:TargetFramework%? ?%compile:Runtime%?" ] + } } diff --git a/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestApp/TestApp.xproj b/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestApp/TestApp.xproj index 4cef17daa..75b98c80d 100644 --- a/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestApp/TestApp.xproj +++ b/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestApp/TestApp.xproj @@ -5,7 +5,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 58808bbc-371e-47d6-a3d0-4902145eda4e TestApp @@ -16,5 +16,5 @@ 2.0 - + diff --git a/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestApp/project.json b/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestApp/project.json index a1cb82f95..efafdef41 100644 --- a/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestApp/project.json +++ b/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestApp/project.json @@ -1,24 +1,27 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true, - "preserveCompilationContext": true + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true, + "preserveCompilationContext": true + }, + "dependencies": { + "TestLibrary": { + "target": "project", + "version": "1.0.0-*" }, - - "dependencies": { - "TestLibrary": { "target":"project", "version":"1.0.0-*" }, - - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } - }, - - "scripts": { - "prepublish" : ["echo prepublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:Framework%? ?%publish:Runtime%?"], - "postpublish" : ["echo postpublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:Framework%? ?%publish:Runtime%?"] + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + }, + "scripts": { + "prepublish": [ + "echo prepublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:Framework%? ?%publish:Runtime%?" + ], + "postpublish": [ + "echo postpublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:Framework%? ?%publish:Runtime%?" + ] + } } diff --git a/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibrary/TestLibrary.xproj b/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibrary/TestLibrary.xproj index eb9f8bc2d..ae7910d0d 100644 --- a/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibrary/TestLibrary.xproj +++ b/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibrary/TestLibrary.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 947dd232-8d9b-4b78-9c6a-94f807d2dd58 TestLibrary @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibrary/project.json b/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibrary/project.json index dce6a0dad..f0b60ee88 100644 --- a/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibrary/project.json +++ b/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibrary/project.json @@ -1,17 +1,20 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "nowarn": [ "CS1591" ], - "xmlDoc": true, - "additionalArguments": [ "-highentropyva+" ] - }, - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "nowarn": [ + "CS1591" + ], + "xmlDoc": true, + "additionalArguments": [ + "-highentropyva+" + ] + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibrary2/project.json b/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibrary2/project.json index 06a4d2157..bc94bb833 100644 --- a/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibrary2/project.json +++ b/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibrary2/project.json @@ -1,18 +1,18 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "TestLibraryWithAppDependency": { + "target": "project", + "version": "1.0.0-*" }, - - "dependencies": { - "TestLibraryWithAppDependency": { "target":"project", "version":"1.0.0-*" }, - - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibraryWithAppDependency/project.json b/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibraryWithAppDependency/project.json index 42c1b5f55..9b7ae73b7 100644 --- a/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibraryWithAppDependency/project.json +++ b/TestAssets/TestProjects/TestAppWithTransitiveAppDependency/TestLibraryWithAppDependency/project.json @@ -1,14 +1,15 @@ { - "version": "1.0.0-*", - "dependencies": { - "TestApp": { "target":"project", "version":"1.0.0-*" }, - - "NETStandard.Library": "1.0.0-rc2-23901" + "version": "1.0.0-*", + "dependencies": { + "TestApp": { + "target": "project", + "version": "1.0.0-*" }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestAppWithWrapperProjectDependency/TestApp/TestApp.xproj b/TestAssets/TestProjects/TestAppWithWrapperProjectDependency/TestApp/TestApp.xproj index 4cef17daa..75b98c80d 100644 --- a/TestAssets/TestProjects/TestAppWithWrapperProjectDependency/TestApp/TestApp.xproj +++ b/TestAssets/TestProjects/TestAppWithWrapperProjectDependency/TestApp/TestApp.xproj @@ -5,7 +5,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 58808bbc-371e-47d6-a3d0-4902145eda4e TestApp @@ -16,5 +16,5 @@ 2.0 - + diff --git a/TestAssets/TestProjects/TestAppWithWrapperProjectDependency/TestApp/project.json b/TestAssets/TestProjects/TestAppWithWrapperProjectDependency/TestApp/project.json index f9f77f358..88dd7cbee 100644 --- a/TestAssets/TestProjects/TestAppWithWrapperProjectDependency/TestApp/project.json +++ b/TestAssets/TestProjects/TestAppWithWrapperProjectDependency/TestApp/project.json @@ -1,19 +1,19 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true, - "preserveCompilationContext": true + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true, + "preserveCompilationContext": true + }, + "dependencies": { + "TestLibrary": { + "target": "project", + "version": "1.0.0-*" }, - - "dependencies": { - "TestLibrary": { "target":"project", "version":"1.0.0-*" }, - - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestBindingRedirectGeneration/TestLibraryGreater/project.json b/TestAssets/TestProjects/TestBindingRedirectGeneration/TestLibraryGreater/project.json index 4b957d224..6254293c5 100644 --- a/TestAssets/TestProjects/TestBindingRedirectGeneration/TestLibraryGreater/project.json +++ b/TestAssets/TestProjects/TestBindingRedirectGeneration/TestLibraryGreater/project.json @@ -1,20 +1,19 @@ -{ - "version": "1.0.0-*", - "testRunner": "xunit", - "dependencies": { - "Newtonsoft.Json": "7.0.1" - }, - - "frameworks": { - "net451": { }, - "netstandardapp1.5": { - "imports": [ - "dnxcore50", - "portable-net45+wp80+win8" - ], - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - } - } +{ + "version": "1.0.0-*", + "testRunner": "xunit", + "dependencies": { + "Newtonsoft.Json": "7.0.1" + }, + "frameworks": { + "net451": {}, + "netstandardapp1.5": { + "imports": [ + "dnxcore50", + "portable-net45+wp80+win8" + ], + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + } } + } } diff --git a/TestAssets/TestProjects/TestBindingRedirectGeneration/TestLibraryLesser/project.json b/TestAssets/TestProjects/TestBindingRedirectGeneration/TestLibraryLesser/project.json index 0cfc9606d..f1bb8120d 100644 --- a/TestAssets/TestProjects/TestBindingRedirectGeneration/TestLibraryLesser/project.json +++ b/TestAssets/TestProjects/TestBindingRedirectGeneration/TestLibraryLesser/project.json @@ -1,21 +1,22 @@ { - "version": "1.0.0-*", - "testRunner": "xunit", - "dependencies": { - "Newtonsoft.Json": "6.0.0", - "TestLibraryGreater": {"target":"project"} - }, - - "frameworks": { - "net451": { }, - "netstandardapp1.5": { - "imports": [ - "dnxcore50", - "portable-net45+wp80+win8" - ], - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - } - } + "version": "1.0.0-*", + "testRunner": "xunit", + "dependencies": { + "Newtonsoft.Json": "6.0.0", + "TestLibraryGreater": { + "target": "project" } + }, + "frameworks": { + "net451": {}, + "netstandardapp1.5": { + "imports": [ + "dnxcore50", + "portable-net45+wp80+win8" + ], + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + } + } + } } diff --git a/TestAssets/TestProjects/TestLibraryWithAnalyzer/project.json b/TestAssets/TestProjects/TestLibraryWithAnalyzer/project.json index 126757ed1..930858382 100644 --- a/TestAssets/TestProjects/TestLibraryWithAnalyzer/project.json +++ b/TestAssets/TestProjects/TestLibraryWithAnalyzer/project.json @@ -1,17 +1,18 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.Runtime.Analyzers": { "version": "1.1.0", "type": "build" } - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "System.Runtime.Analyzers": { + "version": "1.1.0", + "type": "build" } + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" + } + } } diff --git a/TestAssets/TestProjects/TestLibraryWithConfiguration/project.json b/TestAssets/TestProjects/TestLibraryWithConfiguration/project.json index 731ac1d10..a4c57dce6 100644 --- a/TestAssets/TestProjects/TestLibraryWithConfiguration/project.json +++ b/TestAssets/TestProjects/TestLibraryWithConfiguration/project.json @@ -1,21 +1,23 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "nowarn": [ "CS1591" ], - "xmlDoc": true, - "additionalArguments": [ "-highentropyva+" ] - }, - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - "configurations": { - "Test": { - - } - }, - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "nowarn": [ + "CS1591" + ], + "xmlDoc": true, + "additionalArguments": [ + "-highentropyva+" + ] + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "configurations": { + "Test": {} + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestLibraryWithMultipleFrameworks/project.json b/TestAssets/TestProjects/TestLibraryWithMultipleFrameworks/project.json index 391a5fc11..f12c8dafa 100644 --- a/TestAssets/TestProjects/TestLibraryWithMultipleFrameworks/project.json +++ b/TestAssets/TestProjects/TestLibraryWithMultipleFrameworks/project.json @@ -1,21 +1,19 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": false - }, - - "dependencies": { }, - - "frameworks": { - "net20": { }, - "net35": { }, - "net40": { }, - "net461": { }, - "netstandardapp1.5": { - "imports": "dnxcore50", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - } - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": false + }, + "dependencies": {}, + "frameworks": { + "net20": {}, + "net35": {}, + "net40": {}, + "net461": {}, + "netstandardapp1.5": { + "imports": "dnxcore50", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + } } + } } diff --git a/TestAssets/TestProjects/TestMicrosoftCSharpReference/project.json b/TestAssets/TestProjects/TestMicrosoftCSharpReference/project.json index 8f97305eb..8d78b8ecc 100644 --- a/TestAssets/TestProjects/TestMicrosoftCSharpReference/project.json +++ b/TestAssets/TestProjects/TestMicrosoftCSharpReference/project.json @@ -1,17 +1,17 @@ { - "version": "1.0.0", - "dependencies": { }, - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - } - }, - "dnx451": { - "dependencies": { - "Microsoft.CSharp": "4.0.1-*" - } - } + "version": "1.0.0", + "dependencies": {}, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + } + }, + "dnx451": { + "dependencies": { + "Microsoft.CSharp": "4.0.1-rc2-23911" + } } -} \ No newline at end of file + } +} diff --git a/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L0/project.json b/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L0/project.json index ef971a748..06f18fa20 100644 --- a/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L0/project.json +++ b/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L0/project.json @@ -1,19 +1,16 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "L11": "1.0.0-*", - "L12": "1.0.0-*", - - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "L11": "1.0.0-*", + "L12": "1.0.0-*", + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L11/project.json b/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L11/project.json index ed55b003b..ed65c4c04 100644 --- a/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L11/project.json +++ b/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L11/project.json @@ -1,16 +1,13 @@ { - "version": "1.0.0-*", - - "dependencies": { - "L12": "1.0.0-*", - "L21": "1.0.0-*", - - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "dependencies": { + "L12": "1.0.0-*", + "L21": "1.0.0-*", + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L12/project.json b/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L12/project.json index ac03a9cf7..e9486914f 100644 --- a/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L12/project.json +++ b/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L12/project.json @@ -1,15 +1,12 @@ { - "version": "1.0.0-*", - - "dependencies": { - "L22": "1.0.0-*", - - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "dependencies": { + "L22": "1.0.0-*", + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L21/project.json b/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L21/project.json index 8af014e51..155cb2f02 100644 --- a/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L21/project.json +++ b/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L21/project.json @@ -1,13 +1,11 @@ { - "version": "1.0.0-*", - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L22/project.json b/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L22/project.json index 8af014e51..155cb2f02 100644 --- a/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L22/project.json +++ b/TestAssets/TestProjects/TestProjectToProjectDependencies/src/L22/project.json @@ -1,13 +1,11 @@ { - "version": "1.0.0-*", - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestProjectWithCultureSpecificResource/TestProjectWithCultureSpecificResource.xproj b/TestAssets/TestProjects/TestProjectWithCultureSpecificResource/TestProjectWithCultureSpecificResource.xproj index d8121765f..f09a83c44 100644 --- a/TestAssets/TestProjects/TestProjectWithCultureSpecificResource/TestProjectWithCultureSpecificResource.xproj +++ b/TestAssets/TestProjects/TestProjectWithCultureSpecificResource/TestProjectWithCultureSpecificResource.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + fea4ab27-d004-4580-8abe-b171e30b68cc TestProjectWithCultureSpecificResource @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/TestAssets/TestProjects/TestProjectWithCultureSpecificResource/project.json b/TestAssets/TestProjects/TestProjectWithCultureSpecificResource/project.json index c02d0dffe..2e5ebbe30 100644 --- a/TestAssets/TestProjects/TestProjectWithCultureSpecificResource/project.json +++ b/TestAssets/TestProjects/TestProjectWithCultureSpecificResource/project.json @@ -1,16 +1,14 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestProjectWithResource/TestProjectWithResource.xproj b/TestAssets/TestProjects/TestProjectWithResource/TestProjectWithResource.xproj index aee14e286..cc3bb2c74 100644 --- a/TestAssets/TestProjects/TestProjectWithResource/TestProjectWithResource.xproj +++ b/TestAssets/TestProjects/TestProjectWithResource/TestProjectWithResource.xproj @@ -5,7 +5,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 58808bbc-371e-47d6-a3d0-4909876ed864 TestProjectWithResource @@ -16,5 +16,5 @@ 2.0 - + diff --git a/TestAssets/TestProjects/TestProjectWithResource/project.json b/TestAssets/TestProjects/TestProjectWithResource/project.json index c02d0dffe..2e5ebbe30 100644 --- a/TestAssets/TestProjects/TestProjectWithResource/project.json +++ b/TestAssets/TestProjects/TestProjectWithResource/project.json @@ -1,16 +1,14 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/TestAssets/TestProjects/TestSimpleIncrementalApp/TestApp.xproj b/TestAssets/TestProjects/TestSimpleIncrementalApp/TestApp.xproj index 088568e54..518aa7600 100644 --- a/TestAssets/TestProjects/TestSimpleIncrementalApp/TestApp.xproj +++ b/TestAssets/TestProjects/TestSimpleIncrementalApp/TestApp.xproj @@ -5,7 +5,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 58808bbc-371e-47d6-a3d0-4902145edaaa TestSimpleIncrementalApp @@ -16,5 +16,5 @@ 2.0 - + diff --git a/TestAssets/TestProjects/TestSimpleIncrementalApp/project.json b/TestAssets/TestProjects/TestSimpleIncrementalApp/project.json index 3605210ec..7c888a5ad 100644 --- a/TestAssets/TestProjects/TestSimpleIncrementalApp/project.json +++ b/TestAssets/TestProjects/TestSimpleIncrementalApp/project.json @@ -1,17 +1,15 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true, - "xmlDoc": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true, + "xmlDoc": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/packaging/host/windows/host.wxs b/packaging/host/windows/host.wxs index 736beb665..a2bc03f95 100644 --- a/packaging/host/windows/host.wxs +++ b/packaging/host/windows/host.wxs @@ -38,6 +38,7 @@ + diff --git a/packaging/host/windows/registrykeys.wxs b/packaging/host/windows/registrykeys.wxs index 6f5a3cad0..3d971599b 100644 --- a/packaging/host/windows/registrykeys.wxs +++ b/packaging/host/windows/registrykeys.wxs @@ -10,16 +10,18 @@ + - + + diff --git a/packaging/windows/bundle.wxs b/packaging/windows/bundle.wxs index 6999e8a3c..664087a99 100644 --- a/packaging/windows/bundle.wxs +++ b/packaging/windows/bundle.wxs @@ -32,7 +32,13 @@ - + + + + + + + diff --git a/packaging/windows/generatebundle.ps1 b/packaging/windows/generatebundle.ps1 index e50300363..dd73daaaa 100644 --- a/packaging/windows/generatebundle.ps1 +++ b/packaging/windows/generatebundle.ps1 @@ -2,7 +2,9 @@ # Licensed under the MIT license. See LICENSE file in the project root for full license information. param( - [Parameter(Mandatory=$true)][string]$DotnetMSIFile, + [Parameter(Mandatory=$true)][string]$CLISDKMSIFile, + [Parameter(Mandatory=$true)][string]$SharedFxMSIFile, + [Parameter(Mandatory=$true)][string]$SharedHostMSIFile, [Parameter(Mandatory=$true)][string]$DotnetBundleOutput, [Parameter(Mandatory=$true)][string]$WixRoot, [Parameter(Mandatory=$true)][string]$DotnetMSIVersion, @@ -28,7 +30,9 @@ function RunCandleForBundle -dBuildVersion="$DotnetMSIVersion" ` -dDisplayVersion="$DotnetCLIVersion" ` -dReleaseSuffix="$ReleaseSuffix" ` - -dMsiSourcePath="$DotnetMSIFile" ` + -dCLISDKMsiSourcePath="$CLISDKMSIFile" ` + -dSharedFXMsiSourcePath="$SharedFxMSIFile" ` + -dSharedHostMsiSourcePath="$SharedHostMSIFile" ` -arch "$Architecture" ` -ext WixBalExtension.dll ` -ext WixUtilExtension.dll ` @@ -73,9 +77,9 @@ function RunLightForBundle } -if(!(Test-Path $DotnetMSIFile)) +if(!(Test-Path $CLISDKMSIFile)) { - throw "$DotnetMSIFile not found" + throw "$CLISDKMSIFile not found" } Write-Host "Creating dotnet Bundle at $DotnetBundleOutput" @@ -103,6 +107,6 @@ if(!(Test-Path $DotnetBundleOutput)) Write-Host -ForegroundColor Green "Successfully created dotnet bundle - $DotnetBundleOutput" -_ $RepoRoot\test\Installer\testmsi.ps1 @("$DotnetMSIFile") +_ $RepoRoot\test\Installer\testmsi.ps1 @("$CLISDKMSIFile") exit $LastExitCode diff --git a/packaging/windows/registrykeys.wxs b/packaging/windows/registrykeys.wxs index 9bd84eb25..efb5c2b39 100644 --- a/packaging/windows/registrykeys.wxs +++ b/packaging/windows/registrykeys.wxs @@ -23,8 +23,6 @@ - - diff --git a/scripts/Microsoft.DotNet.Cli.Build.Framework/Microsoft.DotNet.Cli.Build.Framework.xproj b/scripts/Microsoft.DotNet.Cli.Build.Framework/Microsoft.DotNet.Cli.Build.Framework.xproj index d4c58d0e8..5e45f32ed 100644 --- a/scripts/Microsoft.DotNet.Cli.Build.Framework/Microsoft.DotNet.Cli.Build.Framework.xproj +++ b/scripts/Microsoft.DotNet.Cli.Build.Framework/Microsoft.DotNet.Cli.Build.Framework.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 49beb486-ab5a-4416-91ea-8cd34abb0c9d Microsoft.DotNet.Cli.Build.Framework @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/scripts/Microsoft.DotNet.Cli.Build.Framework/project.json b/scripts/Microsoft.DotNet.Cli.Build.Framework/project.json index 85a4e9c85..cb16f68b2 100644 --- a/scripts/Microsoft.DotNet.Cli.Build.Framework/project.json +++ b/scripts/Microsoft.DotNet.Cli.Build.Framework/project.json @@ -1,15 +1,13 @@ { - "version": "1.0.0-*", - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.Diagnostics.Process": "4.1.0-rc2-23901", - "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537" - }, - - "frameworks": { - "netstandard1.3": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "System.Diagnostics.Process": "4.1.0-rc2-23911", + "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537" + }, + "frameworks": { + "netstandard1.3": { + "imports": "dnxcore50" } + } } diff --git a/scripts/dotnet-cli-build/CompileTargets.cs b/scripts/dotnet-cli-build/CompileTargets.cs index 3391a649c..7c6c80bb4 100644 --- a/scripts/dotnet-cli-build/CompileTargets.cs +++ b/scripts/dotnet-cli-build/CompileTargets.cs @@ -12,7 +12,7 @@ namespace Microsoft.DotNet.Cli.Build { public class CompileTargets { - public static readonly string CoreCLRVersion = "1.0.2-rc2-23901"; + public static readonly string CoreCLRVersion = "1.0.2-rc2-23911"; public static readonly string AppDepSdkVersion = "1.0.6-prerelease-00003"; public static readonly bool IsWinx86 = CurrentPlatform.IsWindows && CurrentArchitecture.Isx86; @@ -70,15 +70,20 @@ namespace Microsoft.DotNet.Cli.Build var configuration = c.BuildContext.Get("Configuration"); // Run the build + string version = DotNetCli.Stage0.Exec("", "--version").CaptureStdOut().Execute().StdOut; + string rid = Array.Find(version.Split(Environment.NewLine.ToCharArray()), (e) => e.Contains("Runtime Id:")).Replace("Runtime Id:", "").Trim(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // Why does Windows directly call cmake but Linux/Mac calls "build.sh" in the corehost dir? // See the comment in "src/corehost/build.sh" for details. It doesn't work for some reason. var visualStudio = IsWinx86 ? "Visual Studio 14 2015" : "Visual Studio 14 2015 Win64"; var archMacro = IsWinx86 ? "-DCLI_CMAKE_PLATFORM_ARCH_I386=1" : "-DCLI_CMAKE_PLATFORM_ARCH_AMD64=1"; + var ridMacro = $"-DCLI_CMAKE_RUNTIME_ID:STRING={rid}"; + ExecIn(cmakeOut, "cmake", Path.Combine(c.BuildContext.BuildDirectory, "src", "corehost"), archMacro, + ridMacro, "-G", visualStudio); @@ -101,14 +106,21 @@ namespace Microsoft.DotNet.Cli.Build File.Copy(Path.Combine(cmakeOut, "cli", configuration, "corehost.pdb"), Path.Combine(Dirs.Corehost, "corehost.pdb"), overwrite: true); File.Copy(Path.Combine(cmakeOut, "cli", "dll", configuration, "hostpolicy.dll"), Path.Combine(Dirs.Corehost, "hostpolicy.dll"), overwrite: true); File.Copy(Path.Combine(cmakeOut, "cli", "dll", configuration, "hostpolicy.pdb"), Path.Combine(Dirs.Corehost, "hostpolicy.pdb"), overwrite: true); + File.Copy(Path.Combine(cmakeOut, "cli", "fxr", configuration, "hostfxr.dll"), Path.Combine(Dirs.Corehost, "hostfxr.dll"), overwrite: true); + File.Copy(Path.Combine(cmakeOut, "cli", "fxr", configuration, "hostfxr.pdb"), Path.Combine(Dirs.Corehost, "hostfxr.pdb"), overwrite: true); } else { - ExecIn(cmakeOut, Path.Combine(c.BuildContext.BuildDirectory, "src", "corehost", "build.sh")); + ExecIn(cmakeOut, Path.Combine(c.BuildContext.BuildDirectory, "src", "corehost", "build.sh"), + "--arch", + "amd64", + "--rid", + rid); // Copy the output out File.Copy(Path.Combine(cmakeOut, "cli", "corehost"), Path.Combine(Dirs.Corehost, "corehost"), overwrite: true); File.Copy(Path.Combine(cmakeOut, "cli", "dll", $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), overwrite: true); + File.Copy(Path.Combine(cmakeOut, "cli", "fxr", $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}"), Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}"), overwrite: true); } return c.Success(); @@ -193,6 +205,7 @@ namespace Microsoft.DotNet.Cli.Build // Copy corehost File.Copy(Path.Combine(Dirs.Corehost, $"corehost{Constants.ExeSuffix}"), Path.Combine(binDir, $"corehost{Constants.ExeSuffix}"), overwrite: true); File.Copy(Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), Path.Combine(binDir, $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), overwrite: true); + File.Copy(Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}"), Path.Combine(binDir, $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}"), overwrite: true); // Corehostify binaries foreach (var binaryToCorehostify in BinariesForCoreHost) @@ -335,7 +348,7 @@ namespace Microsoft.DotNet.Cli.Build private static List GetAssembliesToCrossGen() { - var list = new List + return new List { "System.Collections.Immutable.dll", "System.Reflection.Metadata.dll", @@ -345,15 +358,6 @@ namespace Microsoft.DotNet.Cli.Build "csc.dll", "vbc.dll" }; - - // mscorlib is already crossgenned on Windows - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // mscorlib has to be crossgenned first - list.Insert(0, "mscorlib.dll"); - } - - return list; } } } diff --git a/scripts/dotnet-cli-build/MsiTargets.cs b/scripts/dotnet-cli-build/MsiTargets.cs index e0e3ecb37..aa50fcac8 100644 --- a/scripts/dotnet-cli-build/MsiTargets.cs +++ b/scripts/dotnet-cli-build/MsiTargets.cs @@ -94,9 +94,10 @@ namespace Microsoft.DotNet.Cli.Build [BuildPlatforms(BuildPlatform.Windows)] public static BuildTargetResult GenerateCliSdkMsi(BuildTargetContext c) { + var cliSdkRoot = c.BuildContext.Get("CLISDKRoot"); Cmd("powershell", "-NoProfile", "-NoLogo", Path.Combine(Dirs.RepoRoot, "packaging", "windows", "generatemsi.ps1"), - Dirs.Stage2, SdkMsi, WixRoot, MsiVersion, CliVersion, Arch, Channel) + cliSdkRoot, SdkMsi, WixRoot, MsiVersion, CliVersion, Arch, Channel) .Execute() .EnsureSuccessful(); return c.Success(); @@ -156,7 +157,7 @@ namespace Microsoft.DotNet.Cli.Build { Cmd("powershell", "-NoProfile", "-NoLogo", Path.Combine(Dirs.RepoRoot, "packaging", "windows", "generatebundle.ps1"), - SdkMsi, SdkBundle, WixRoot, MsiVersion, CliVersion, Arch, Channel) + SdkMsi, SharedFrameworkMsi, SharedHostMsi, SdkBundle, WixRoot, MsiVersion, CliVersion, Arch, Channel) .EnvironmentVariable("Stage2Dir", Dirs.Stage2) .Execute() .EnsureSuccessful(); diff --git a/scripts/dotnet-cli-build/PackageTargets.cs b/scripts/dotnet-cli-build/PackageTargets.cs index 11ba01b43..4b54013cf 100644 --- a/scripts/dotnet-cli-build/PackageTargets.cs +++ b/scripts/dotnet-cli-build/PackageTargets.cs @@ -22,6 +22,7 @@ namespace Microsoft.DotNet.Cli.Build [Target(nameof(PrepareTargets.Init), nameof(PackageTargets.InitPackage), nameof(PackageTargets.GenerateVersionBadge), + nameof(PackageTargets.CopyCLISDKLayout), nameof(SharedFrameworkTargets.PublishSharedHost), nameof(SharedFrameworkTargets.PublishSharedFramework), nameof(PackageTargets.GenerateCompressedFile), @@ -47,6 +48,52 @@ namespace Microsoft.DotNet.Cli.Build return c.Success(); } + [Target] + public static BuildTargetResult CopyCLISDKLayout(BuildTargetContext c) + { + var nugetVersion = c.BuildContext.Get("BuildVersion").NuGetVersion; + var cliSdkRoot = Path.Combine(Dirs.Output, "obj", "clisdk"); + var cliSdk = Path.Combine(cliSdkRoot, "sdk", nugetVersion); + + if (Directory.Exists(cliSdkRoot)) + { + string[] files = Directory.GetFiles(cliSdkRoot, "*", SearchOption.AllDirectories); + foreach (string file in files) + { + File.SetAttributes(file, FileAttributes.Normal); + File.Delete(file); + } + + Directory.Delete(cliSdkRoot, true); + } + + Directory.CreateDirectory(cliSdk); + + var binPath = Path.Combine(Dirs.Stage2, "bin"); + foreach (var file in Directory.GetFiles(binPath, "*", SearchOption.AllDirectories)) + { + string destFile = file.Replace(binPath, cliSdk); + Directory.CreateDirectory(Path.GetDirectoryName(destFile)); + File.Copy(file, destFile, true); + } + + File.Copy(Path.Combine(Dirs.Stage2, ".version"), Path.Combine(cliSdk, ".version"), true); + + // copy stage2 to "cliSdkRoot\bin". + // this is a temp hack until we fix the build scripts to use the new shared fx and shared host + // the current build scripts need the CLI sdk to be in the bin folder. + + foreach (var file in Directory.GetFiles(Dirs.Stage2, "*", SearchOption.AllDirectories)) + { + string destFile = file.Replace(Dirs.Stage2, cliSdkRoot); + Directory.CreateDirectory(Path.GetDirectoryName(destFile)); + File.Copy(file, destFile, true); + } + + c.BuildContext["CLISDKRoot"] = cliSdkRoot; + return c.Success(); + } + [Target(nameof(PackageTargets.GenerateZip), nameof(PackageTargets.GenerateTarBall))] public static BuildTargetResult GenerateCompressedFile(BuildTargetContext c) { @@ -59,7 +106,7 @@ namespace Microsoft.DotNet.Cli.Build { CreateZipFromDirectory(c.BuildContext.Get("SharedHostPublishRoot"), c.BuildContext.Get("SharedHostCompressedFile")); CreateZipFromDirectory(c.BuildContext.Get("SharedFrameworkPublishRoot"), c.BuildContext.Get("SharedFrameworkCompressedFile")); - CreateZipFromDirectory(Dirs.Stage2, c.BuildContext.Get("SdkCompressedFile")); + CreateZipFromDirectory(c.BuildContext.Get("CLISDKRoot"), c.BuildContext.Get("SdkCompressedFile")); return c.Success(); } @@ -70,7 +117,7 @@ namespace Microsoft.DotNet.Cli.Build { CreateTarBallFromDirectory(c.BuildContext.Get("SharedHostPublishRoot"), c.BuildContext.Get("SharedHostCompressedFile")); CreateTarBallFromDirectory(c.BuildContext.Get("SharedFrameworkPublishRoot"), c.BuildContext.Get("SharedFrameworkCompressedFile")); - CreateTarBallFromDirectory(Dirs.Stage2, c.BuildContext.Get("SdkCompressedFile")); + CreateTarBallFromDirectory(c.BuildContext.Get("CLISDKRoot"), c.BuildContext.Get("SdkCompressedFile")); return c.Success(); } diff --git a/scripts/dotnet-cli-build/PrepareTargets.cs b/scripts/dotnet-cli-build/PrepareTargets.cs index 08dca3e05..1c87e132f 100644 --- a/scripts/dotnet-cli-build/PrepareTargets.cs +++ b/scripts/dotnet-cli-build/PrepareTargets.cs @@ -190,8 +190,8 @@ namespace Microsoft.DotNet.Cli.Build { var dotnet = DotNetCli.Stage0; - dotnet.Restore().WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "src")).Execute().EnsureSuccessful(); - dotnet.Restore().WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "tools")).Execute().EnsureSuccessful(); + dotnet.Restore("--verbosity", "verbose", "--disable-parallel").WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "src")).Execute().EnsureSuccessful(); + dotnet.Restore("--verbosity", "verbose", "--disable-parallel").WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "tools")).Execute().EnsureSuccessful(); return c.Success(); } diff --git a/scripts/dotnet-cli-build/PublishTargets.cs b/scripts/dotnet-cli-build/PublishTargets.cs index b90e0c56c..b624656ad 100644 --- a/scripts/dotnet-cli-build/PublishTargets.cs +++ b/scripts/dotnet-cli-build/PublishTargets.cs @@ -46,6 +46,9 @@ namespace Microsoft.DotNet.Cli.Build [Target(nameof(PublishTargets.PublishVersionBadge), nameof(PublishTargets.PublishCompressedFile), nameof(PublishTargets.PublishSdkInstallerFile), + nameof(PublishTargets.PublishDebFileToDebianRepo), + nameof(PublishTargets.PublishSharedFrameworkCompressedFile), + nameof(PublishTargets.PublishSharedHostCompressedFile), nameof(PublishTargets.PublishLatestVersionTextFile))] public static BuildTargetResult PublishArtifacts(BuildTargetContext c) { @@ -140,6 +143,32 @@ namespace Microsoft.DotNet.Cli.Build return uploadJson; } + [Target] + public static BuildTargetResult PublishSharedFrameworkCompressedFile(BuildTargetContext c) + { + var compressedFile = c.BuildContext.Get("SharedFrameworkCompressedFile"); + var compressedFileBlob = $"{Channel}/Binaries/{Version}/{Path.GetFileName(compressedFile)}"; + var latestCompressedFile = compressedFile.Replace(Version, "latest"); + var latestCompressedFileBlob = $"{Channel}/Binaries/Latest/{Path.GetFileName(latestCompressedFile)}"; + + PublishFileAzure(compressedFileBlob, compressedFile); + PublishFileAzure(latestCompressedFileBlob, compressedFile); + return c.Success(); + } + + [Target] + public static BuildTargetResult PublishSharedHostCompressedFile(BuildTargetContext c) + { + var compressedFile = c.BuildContext.Get("SharedHostCompressedFile"); + var compressedFileBlob = $"{Channel}/Binaries/{Version}/{Path.GetFileName(compressedFile)}"; + var latestCompressedFile = compressedFile.Replace(Version, "latest"); + var latestCompressedFileBlob = $"{Channel}/Binaries/Latest/{Path.GetFileName(latestCompressedFile)}"; + + PublishFileAzure(compressedFileBlob, compressedFile); + PublishFileAzure(latestCompressedFileBlob, compressedFile); + return c.Success(); + } + private static BuildTargetResult PublishFile(BuildTargetContext c, string file) { var env = PackageTargets.GetCommonEnvVars(c); diff --git a/scripts/dotnet-cli-build/SharedFrameworkTargets.cs b/scripts/dotnet-cli-build/SharedFrameworkTargets.cs index a2ff2e67c..2892bc2f2 100644 --- a/scripts/dotnet-cli-build/SharedFrameworkTargets.cs +++ b/scripts/dotnet-cli-build/SharedFrameworkTargets.cs @@ -9,6 +9,8 @@ using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.Extensions.PlatformAbstractions; using static Microsoft.DotNet.Cli.Build.Framework.BuildHelpers; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; namespace Microsoft.DotNet.Cli.Build { @@ -17,6 +19,8 @@ namespace Microsoft.DotNet.Cli.Build public const string SharedFrameworkName = "Microsoft.NETCore.App"; private const string CoreHostBaseName = "corehost"; + private const string DotnetHostFxrBaseName = "hostfxr"; + private const string HostPolicyBaseName = "hostpolicy"; [Target(nameof(PackageSharedFramework), nameof(CrossGenAllManagedAssemblies))] public static BuildTargetResult PublishSharedFramework(BuildTargetContext c) @@ -63,16 +67,36 @@ namespace Microsoft.DotNet.Cli.Build File.Delete(Path.Combine(SharedFrameworkNameAndVersionRoot, $"framework{Constants.ExeSuffix}")); File.Delete(Path.Combine(SharedFrameworkNameAndVersionRoot, "framework.dll")); File.Delete(Path.Combine(SharedFrameworkNameAndVersionRoot, "framework.pdb")); + File.Delete(Path.Combine(SharedFrameworkNameAndVersionRoot, "framework.runtimeconfig.json")); // Rename the .deps file + var destinationDeps = Path.Combine(SharedFrameworkNameAndVersionRoot, $"{SharedFrameworkName}.deps.json"); File.Move(Path.Combine(SharedFrameworkNameAndVersionRoot, "framework.deps"), Path.Combine(SharedFrameworkNameAndVersionRoot, $"{SharedFrameworkName}.deps")); - File.Move(Path.Combine(SharedFrameworkNameAndVersionRoot, "framework.deps.json"), Path.Combine(SharedFrameworkNameAndVersionRoot, $"{SharedFrameworkName}.deps.json")); + File.Move(Path.Combine(SharedFrameworkNameAndVersionRoot, "framework.deps.json"), destinationDeps); - // corehost will be renamed to dotnet at some point and then this can be removed. - File.Move(Path.Combine(SharedFrameworkNameAndVersionRoot, $"{CoreHostBaseName}{Constants.ExeSuffix}"), Path.Combine(SharedFrameworkNameAndVersionRoot, $"dotnet{Constants.ExeSuffix}")); + // Merge in the RID fallback graph + var fallbackFileName = PlatformServices.Default.Runtime.OperatingSystemPlatform.ToString().ToLowerInvariant() + ".json"; + var fallbackFile = Path.Combine(Dirs.RepoRoot, "src", "sharedframework", "rid-fallbacks", fallbackFileName); + if (File.Exists(fallbackFile)) + { + c.Info($"Merging in RID fallback graph: {fallbackFile}"); + var deps = JObject.Parse(File.ReadAllText(destinationDeps)); + var ridfallback = JObject.Parse(File.ReadAllText(fallbackFile)); + deps["runtimes"] = ridfallback["runtimes"]; + File.WriteAllText(destinationDeps, deps.ToString(Formatting.Indented)); + } + else + { + c.Warn($"RID fallback graph file not found: {fallbackFile}"); + } - // hostpolicy will be renamed to dotnet at some point and then this can be removed. - File.Move(Path.Combine(SharedFrameworkNameAndVersionRoot, $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), Path.Combine(SharedFrameworkNameAndVersionRoot, $"{Constants.DynamicLibPrefix}dotnethostimpl{Constants.DynamicLibSuffix}")); + // corehost will be renamed to dotnet at some point and then we will not need to rename it here. + File.Copy( + Path.Combine(Dirs.Corehost, $"{CoreHostBaseName}{Constants.ExeSuffix}"), + Path.Combine(SharedFrameworkNameAndVersionRoot, $"dotnet{Constants.ExeSuffix}")); + File.Copy( + Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}{HostPolicyBaseName}{Constants.DynamicLibSuffix}"), + Path.Combine(SharedFrameworkNameAndVersionRoot, $"{Constants.DynamicLibPrefix}{HostPolicyBaseName}{Constants.DynamicLibSuffix}"), true); if (File.Exists(Path.Combine(SharedFrameworkNameAndVersionRoot, "mscorlib.ni.dll"))) { @@ -81,10 +105,6 @@ namespace Microsoft.DotNet.Cli.Build File.Delete(Path.Combine(SharedFrameworkNameAndVersionRoot, "mscorlib.dll")); c.BuildContext["SharedFrameworkNameAndVersionRoot"] = SharedFrameworkNameAndVersionRoot; } - else - { - c.Warn("Shared framework will not be crossgen'd because mscorlib.ni.dll does not exist."); - } return c.Success(); } @@ -93,9 +113,20 @@ namespace Microsoft.DotNet.Cli.Build public static BuildTargetResult PublishSharedHost(BuildTargetContext c) { string SharedHostPublishRoot = Path.Combine(Dirs.Output, "obj", "sharedhost"); + if (Directory.Exists(SharedHostPublishRoot)) + { + Directory.Delete(SharedHostPublishRoot); + } + Directory.CreateDirectory(SharedHostPublishRoot); + // corehost will be renamed to dotnet at some point and then this can be removed. - File.Copy(Path.Combine(Dirs.Corehost, $"{CoreHostBaseName}{Constants.ExeSuffix}"), Path.Combine(SharedHostPublishRoot, $"dotnet{Constants.ExeSuffix}")); + File.Copy( + Path.Combine(Dirs.Corehost, $"{CoreHostBaseName}{Constants.ExeSuffix}"), + Path.Combine(SharedHostPublishRoot, $"dotnet{Constants.ExeSuffix}")); + File.Copy( + Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}{DotnetHostFxrBaseName}{Constants.DynamicLibSuffix}"), + Path.Combine(SharedHostPublishRoot, $"{Constants.DynamicLibPrefix}{DotnetHostFxrBaseName}{Constants.DynamicLibSuffix}")); c.BuildContext["SharedHostPublishRoot"] = SharedHostPublishRoot; diff --git a/scripts/dotnet-cli-build/TestTargets.cs b/scripts/dotnet-cli-build/TestTargets.cs index 375a984be..8526400a3 100644 --- a/scripts/dotnet-cli-build/TestTargets.cs +++ b/scripts/dotnet-cli-build/TestTargets.cs @@ -60,7 +60,7 @@ namespace Microsoft.DotNet.Cli.Build CleanNuGetTempCache(); var dotnet = DotNetCli.Stage2; - dotnet.Restore().WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "TestAssets", "TestPackages")).Execute().EnsureSuccessful(); + dotnet.Restore("--verbosity", "verbose", "--disable-parallel").WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "TestAssets", "TestPackages")).Execute().EnsureSuccessful(); return c.Success(); } @@ -75,16 +75,16 @@ namespace Microsoft.DotNet.Cli.Build var dotnet = DotNetCli.Stage2; - dotnet.Restore("--fallbacksource", Dirs.TestPackages) + dotnet.Restore("--verbosity", "verbose", "--disable-parallel", "--fallbacksource", Dirs.TestPackages) .WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "TestAssets", "TestProjects")) .Execute().EnsureSuccessful(); // The 'ProjectModelServer' directory contains intentionally-unresolved dependencies, so don't check for success. Also, suppress the output - dotnet.Restore() + dotnet.Restore("--verbosity", "verbose", "--disable-parallel") .WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "TestAssets", "ProjectModelServer", "DthTestProjects")) .Execute(); - dotnet.Restore() + dotnet.Restore("--verbosity", "verbose", "--disable-parallel") .WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "TestAssets", "ProjectModelServer", "DthUpdateSearchPathSample")) .Execute(); @@ -152,7 +152,7 @@ namespace Microsoft.DotNet.Cli.Build CleanBinObj(c, Path.Combine(c.BuildContext.BuildDirectory, "test")); CleanNuGetTempCache(); - DotNetCli.Stage2.Restore("--fallbacksource", Dirs.TestPackages) + DotNetCli.Stage2.Restore("--verbosity", "verbose", "--disable-parallel", "--fallbacksource", Dirs.TestPackages) .WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "test")) .Execute() .EnsureSuccessful(); diff --git a/scripts/dotnet-cli-build/dotnet-cli-build.xproj b/scripts/dotnet-cli-build/dotnet-cli-build.xproj index fd8546624..fdd8f182d 100644 --- a/scripts/dotnet-cli-build/dotnet-cli-build.xproj +++ b/scripts/dotnet-cli-build/dotnet-cli-build.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + d7b9695d-23eb-4ea8-b8ab-707a0092e1d5 Microsoft.DotNet.Cli.Build @@ -14,5 +14,5 @@ 2.0 - + \ No newline at end of file diff --git a/scripts/dotnet-cli-build/project.json b/scripts/dotnet-cli-build/project.json index f66171d24..72295b954 100755 --- a/scripts/dotnet-cli-build/project.json +++ b/scripts/dotnet-cli-build/project.json @@ -1,23 +1,18 @@ { - "version": "1.0.0-*", - "description": "Build scripts for dotnet-cli", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression.ZipFile": "4.0.1-rc2-23901", - "System.Security.Cryptography.Algorithms": "4.0.0-rc2-23901", - "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537", - "Microsoft.DotNet.Cli.Build.Framework": "1.0.0-*", - "WindowsAzure.Storage" : "6.2.2-preview", - "System.Reflection.Metadata" : "1.2.0" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": ["dnxcore50", "portable-net45+win8"] - } + "version": "1.0.0-*", + "description": "Build scripts for dotnet-cli", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.Cli.Build.Framework": "1.0.0-*", + "WindowsAzure.Storage": "6.2.2-preview", + "System.Reflection.Metadata": "1.2.0" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": [ "dnxcore50", "portable-net45+win8" ] } + } } diff --git a/scripts/package/projectsToPack.ps1 b/scripts/package/projectsToPack.ps1 index 1b8cbe54f..5c08c9573 100644 --- a/scripts/package/projectsToPack.ps1 +++ b/scripts/package/projectsToPack.ps1 @@ -3,10 +3,13 @@ $ProjectsToPack = @( "Microsoft.DotNet.Cli.Utils", + "Microsoft.DotNet.Compiler.Common", + "Microsoft.DotNet.Files", + "Microsoft.DotNet.InternalAbstractions", "Microsoft.DotNet.ProjectModel", "Microsoft.DotNet.ProjectModel.Loader", "Microsoft.DotNet.ProjectModel.Workspaces", - "Microsoft.DotNet.InternalAbstractions", + "Microsoft.DotNet.TestFramework", "Microsoft.Extensions.DependencyModel", "Microsoft.Extensions.Testing.Abstractions" ) \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandResolution/PackagedCommandSpecFactory.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/PackagedCommandSpecFactory.cs index ee5bd7ecb..8129fa2e5 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandResolution/PackagedCommandSpecFactory.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/PackagedCommandSpecFactory.cs @@ -98,7 +98,8 @@ namespace Microsoft.DotNet.Cli.Utils if (depsFilePath != null) { - arguments.Add($"--depsfile:{depsFilePath}"); + arguments.Add("--depsfile"); + arguments.Add(depsFilePath); } arguments.AddRange(commandArguments); diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectDependenciesCommandResolver.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectDependenciesCommandResolver.cs index bf314ee6a..608beb231 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectDependenciesCommandResolver.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectDependenciesCommandResolver.cs @@ -75,7 +75,7 @@ namespace Microsoft.DotNet.Cli.Utils return null; } - var depsFilePath = projectContext.GetOutputPaths(configuration, outputPath: outputPath).RuntimeFiles.Deps; + var depsFilePath = projectContext.GetOutputPaths(configuration, outputPath: outputPath).RuntimeFiles.DepsJson; var dependencyLibraries = GetAllDependencyLibraries(projectContext); diff --git a/src/Microsoft.DotNet.Cli.Utils/Constants.cs b/src/Microsoft.DotNet.Cli.Utils/Constants.cs index 9bbb22325..13a349b13 100644 --- a/src/Microsoft.DotNet.Cli.Utils/Constants.cs +++ b/src/Microsoft.DotNet.Cli.Utils/Constants.cs @@ -37,7 +37,8 @@ namespace Microsoft.DotNet.Cli.Utils public static readonly string HostExecutableName = "corehost" + ExeSuffix; public static readonly string[] HostBinaryNames = new string[] { HostExecutableName, - (CurrentPlatform == Platform.Windows ? "hostpolicy" : "libhostpolicy") + DynamicLibSuffix + (CurrentPlatform == Platform.Windows ? "hostpolicy" : "libhostpolicy") + DynamicLibSuffix, + (CurrentPlatform == Platform.Windows ? "hostfxr" : "libhostfxr") + DynamicLibSuffix }; } diff --git a/src/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.xproj b/src/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.xproj index 68df775cf..e5fda6870 100644 --- a/src/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.xproj +++ b/src/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.xproj @@ -5,7 +5,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 61b7c351-c77d-43f7-b56f-bb1440178e10 Microsoft.DotNet.Cli.Utils @@ -16,5 +16,5 @@ 2.0 - + diff --git a/src/Microsoft.DotNet.Cli.Utils/project.json b/src/Microsoft.DotNet.Cli.Utils/project.json index 625b48bf4..3e97e0621 100644 --- a/src/Microsoft.DotNet.Cli.Utils/project.json +++ b/src/Microsoft.DotNet.Cli.Utils/project.json @@ -1,27 +1,27 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "keyFile": "../../tools/Key.snk", - "warningsAsErrors": true - }, - "dependencies": { - "Microsoft.DotNet.ProjectModel": "1.0.0-*", - "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537" - }, - "frameworks": { - "net451": { - "frameworkAssemblies": { - "System.Runtime": { - "type": "build" - } - } - }, - "netstandard1.3": { - "imports": "dnxcore50", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.Diagnostics.Process": "4.1.0-rc2-23901" - } + "version": "1.0.0-*", + "compilationOptions": { + "keyFile": "../../tools/Key.snk", + "warningsAsErrors": true + }, + "dependencies": { + "Microsoft.DotNet.ProjectModel": "1.0.0-*", + "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537" + }, + "frameworks": { + "net451": { + "frameworkAssemblies": { + "System.Runtime": { + "type": "build" } + } + }, + "netstandard1.3": { + "imports": "dnxcore50", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "System.Diagnostics.Process": "4.1.0-rc2-23911" + } } + } } diff --git a/src/Microsoft.DotNet.Compiler.Common/Microsoft.Dotnet.Compiler.Common.xproj b/src/Microsoft.DotNet.Compiler.Common/Microsoft.Dotnet.Compiler.Common.xproj index e7b0225ee..50899a805 100644 --- a/src/Microsoft.DotNet.Compiler.Common/Microsoft.Dotnet.Compiler.Common.xproj +++ b/src/Microsoft.DotNet.Compiler.Common/Microsoft.Dotnet.Compiler.Common.xproj @@ -5,7 +5,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + a16958e1-24c7-4f1e-b317-204ad91625dd Microsoft.Dotnet.Cli.Compiler.Common @@ -16,5 +16,5 @@ 2.0 - + diff --git a/src/Microsoft.DotNet.Compiler.Common/project.json b/src/Microsoft.DotNet.Compiler.Common/project.json index 0e15d8637..f0376403a 100644 --- a/src/Microsoft.DotNet.Compiler.Common/project.json +++ b/src/Microsoft.DotNet.Compiler.Common/project.json @@ -1,23 +1,23 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "keyFile": "../../tools/Key.snk" - }, - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.CommandLine": "0.1.0-e160119-1", - "Microsoft.CodeAnalysis.CSharp": "1.2.0-beta1-20160202-02", - "Microsoft.DotNet.ProjectModel": "1.0.0-*", - "Microsoft.DotNet.Cli.Utils": "1.0.0-*", - "Microsoft.DotNet.Files": "1.0.0-*" - }, - "frameworks": { - "netstandard1.3": { - "imports": [ - "dnxcore50", - "portable-net45+win8" - ] - } - }, - "scripts": {} -} \ No newline at end of file + "version": "1.0.0-*", + "compilationOptions": { + "keyFile": "../../tools/Key.snk" + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "System.CommandLine": "0.1.0-e160119-1", + "Microsoft.CodeAnalysis.CSharp": "1.2.0-beta1-20160202-02", + "Microsoft.DotNet.ProjectModel": "1.0.0-*", + "Microsoft.DotNet.Cli.Utils": "1.0.0-*", + "Microsoft.DotNet.Files": "1.0.0-*" + }, + "frameworks": { + "netstandard1.3": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ] + } + }, + "scripts": {} +} diff --git a/src/Microsoft.DotNet.Files/Microsoft.DotNet.Files.xproj b/src/Microsoft.DotNet.Files/Microsoft.DotNet.Files.xproj index 0dca981d4..15a95077a 100644 --- a/src/Microsoft.DotNet.Files/Microsoft.DotNet.Files.xproj +++ b/src/Microsoft.DotNet.Files/Microsoft.DotNet.Files.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + d521dd9f-0614-4929-93b4-d8fa5682c174 Microsoft.DotNet.Files @@ -14,5 +14,5 @@ 2.0 - + \ No newline at end of file diff --git a/src/Microsoft.DotNet.Files/project.json b/src/Microsoft.DotNet.Files/project.json index 82f1a2b51..1131165e9 100644 --- a/src/Microsoft.DotNet.Files/project.json +++ b/src/Microsoft.DotNet.Files/project.json @@ -1,20 +1,20 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "keyFile": "../../tools/Key.snk" - }, - "description": "Abstraction to interact with the file system and file paths.", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.Linq.Expressions": "4.0.11-rc2-23901", - "Microsoft.Extensions.FileSystemGlobbing": "1.0.0-rc2-15996", - "Microsoft.DotNet.Cli.Utils": "1.0.0-*", - "Microsoft.DotNet.ProjectModel": "1.0.0-*" - }, - "frameworks": { - "netstandard1.3": { - "imports": "dnxcore50" - } - }, - "scripts": {} -} \ No newline at end of file + "version": "1.0.0-*", + "compilationOptions": { + "keyFile": "../../tools/Key.snk" + }, + "description": "Abstraction to interact with the file system and file paths.", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "System.Linq.Expressions": "4.0.11-rc2-23911", + "Microsoft.Extensions.FileSystemGlobbing": "1.0.0-rc2-15996", + "Microsoft.DotNet.Cli.Utils": "1.0.0-*", + "Microsoft.DotNet.ProjectModel": "1.0.0-*" + }, + "frameworks": { + "netstandard1.3": { + "imports": "dnxcore50" + } + }, + "scripts": {} +} diff --git a/src/Microsoft.DotNet.InternalAbstractions/FileWrapper.cs b/src/Microsoft.DotNet.InternalAbstractions/FileWrapper.cs index 8b6efbeac..867d4eab1 100644 --- a/src/Microsoft.DotNet.InternalAbstractions/FileWrapper.cs +++ b/src/Microsoft.DotNet.InternalAbstractions/FileWrapper.cs @@ -16,5 +16,10 @@ namespace Microsoft.Extensions.EnvironmentAbstractions { return File.ReadAllText(path); } + + public Stream OpenRead(string path) + { + return File.OpenRead(path); + } } } \ No newline at end of file diff --git a/src/Microsoft.DotNet.InternalAbstractions/IFile.cs b/src/Microsoft.DotNet.InternalAbstractions/IFile.cs index 26d029eec..5b0328de9 100644 --- a/src/Microsoft.DotNet.InternalAbstractions/IFile.cs +++ b/src/Microsoft.DotNet.InternalAbstractions/IFile.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.IO; + namespace Microsoft.Extensions.EnvironmentAbstractions { internal interface IFile @@ -8,5 +10,7 @@ namespace Microsoft.Extensions.EnvironmentAbstractions bool Exists(string path); string ReadAllText(string path); + + Stream OpenRead(string path); } } \ No newline at end of file diff --git a/src/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.xproj b/src/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.xproj index 2f07cc659..2a323b565 100644 --- a/src/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.xproj +++ b/src/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + bd4f0750-4e81-4ad2-90b5-e470881792c3 Microsoft.DotNet.InternalAbstractions @@ -14,5 +14,5 @@ 2.0 - + \ No newline at end of file diff --git a/src/Microsoft.DotNet.InternalAbstractions/project.json b/src/Microsoft.DotNet.InternalAbstractions/project.json index 70b5e7221..3daa0c061 100644 --- a/src/Microsoft.DotNet.InternalAbstractions/project.json +++ b/src/Microsoft.DotNet.InternalAbstractions/project.json @@ -13,14 +13,13 @@ "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537" }, "frameworks": { - "net451": { }, + "net451": {}, "netstandard1.3": { "imports": "dnxcore50", "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" + "NETStandard.Library": "1.5.0-rc2-23911" } } }, - "scripts": { - } + "scripts": {} } diff --git a/src/Microsoft.DotNet.ProjectModel.Loader/Microsoft.DotNet.ProjectModel.Loader.xproj b/src/Microsoft.DotNet.ProjectModel.Loader/Microsoft.DotNet.ProjectModel.Loader.xproj index 049c639a2..51615b07a 100644 --- a/src/Microsoft.DotNet.ProjectModel.Loader/Microsoft.DotNet.ProjectModel.Loader.xproj +++ b/src/Microsoft.DotNet.ProjectModel.Loader/Microsoft.DotNet.ProjectModel.Loader.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + c7af0290-ef0d-44dc-9edc-600803b664f8 Microsoft.DotNet.ProjectModel.Loader @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/src/Microsoft.DotNet.ProjectModel.Loader/project.json b/src/Microsoft.DotNet.ProjectModel.Loader/project.json index efe082435..9d2eca854 100644 --- a/src/Microsoft.DotNet.ProjectModel.Loader/project.json +++ b/src/Microsoft.DotNet.ProjectModel.Loader/project.json @@ -1,16 +1,16 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "keyFile": "../../tools/Key.snk" - }, - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "Microsoft.DotNet.ProjectModel": "1.0.0-*", - "System.Runtime.Loader": "4.0.0-rc2-23901" - }, - "frameworks": { - "netstandard1.3": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "keyFile": "../../tools/Key.snk" + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.ProjectModel": "1.0.0-*", + "System.Runtime.Loader": "4.0.0-rc2-23911" + }, + "frameworks": { + "netstandard1.3": { + "imports": "dnxcore50" } -} \ No newline at end of file + } +} diff --git a/src/Microsoft.DotNet.ProjectModel.Workspaces/Microsoft.DotNet.ProjectModel.Workspaces.xproj b/src/Microsoft.DotNet.ProjectModel.Workspaces/Microsoft.DotNet.ProjectModel.Workspaces.xproj index 3227dea0a..fa941946f 100644 --- a/src/Microsoft.DotNet.ProjectModel.Workspaces/Microsoft.DotNet.ProjectModel.Workspaces.xproj +++ b/src/Microsoft.DotNet.ProjectModel.Workspaces/Microsoft.DotNet.ProjectModel.Workspaces.xproj @@ -4,7 +4,7 @@ 14.0.23107 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + bd7833f8-3209-4682-bf75-b4bca883e279 Microsoft.DotNet.ProjectModel.Workspaces @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/src/Microsoft.DotNet.ProjectModel.Workspaces/project.json b/src/Microsoft.DotNet.ProjectModel.Workspaces/project.json index 83f7a2d6e..e14bbe01b 100644 --- a/src/Microsoft.DotNet.ProjectModel.Workspaces/project.json +++ b/src/Microsoft.DotNet.ProjectModel.Workspaces/project.json @@ -1,20 +1,20 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "keyFile": "../../tools/Key.snk" - }, - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "Microsoft.DotNet.ProjectModel": "1.0.0-*", - "Microsoft.DotNet.Compiler.Common": "1.0.0-*", - "Microsoft.CodeAnalysis.CSharp.Workspaces": "1.2.0-beta1-20160202-02" - }, - "frameworks": { - "netstandard1.3": { - "imports": [ - "dnxcore50", - "portable-net45+win8" - ] - } + "version": "1.0.0-*", + "compilationOptions": { + "keyFile": "../../tools/Key.snk" + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.ProjectModel": "1.0.0-*", + "Microsoft.DotNet.Compiler.Common": "1.0.0-*", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "1.2.0-beta1-20160202-02" + }, + "frameworks": { + "netstandard1.3": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ] } -} \ No newline at end of file + } +} diff --git a/src/Microsoft.DotNet.ProjectModel/DependencyContextBuilder.cs b/src/Microsoft.DotNet.ProjectModel/DependencyContextBuilder.cs index fcfec2cc4..d36ac6f82 100644 --- a/src/Microsoft.DotNet.ProjectModel/DependencyContextBuilder.cs +++ b/src/Microsoft.DotNet.ProjectModel/DependencyContextBuilder.cs @@ -55,7 +55,7 @@ namespace Microsoft.Extensions.DependencyModel compilationOptions, GetLibraries(compilationExports, dependencyLookup, runtime: false).Cast(), GetLibraries(runtimeExports, dependencyLookup, runtime: true).Cast(), - new KeyValuePair[0]); + new RuntimeFallbacks[] {}); } private static CompilationOptions GetCompilationOptions(CommonCompilerOptions compilerOptions) diff --git a/src/Microsoft.DotNet.ProjectModel/Microsoft.DotNet.ProjectModel.xproj b/src/Microsoft.DotNet.ProjectModel/Microsoft.DotNet.ProjectModel.xproj index c33400887..d56e4bae5 100644 --- a/src/Microsoft.DotNet.ProjectModel/Microsoft.DotNet.ProjectModel.xproj +++ b/src/Microsoft.DotNet.ProjectModel/Microsoft.DotNet.ProjectModel.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 303677d5-7312-4c3f-baee-beb1a9bd9fe6 Microsoft.DotNet.ProjectModel @@ -14,5 +14,5 @@ 2.0 - + \ No newline at end of file diff --git a/src/Microsoft.DotNet.ProjectModel/project.json b/src/Microsoft.DotNet.ProjectModel/project.json index 9479b166a..46cd0f97c 100644 --- a/src/Microsoft.DotNet.ProjectModel/project.json +++ b/src/Microsoft.DotNet.ProjectModel/project.json @@ -1,47 +1,47 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "keyFile": "../../tools/Key.snk" + "version": "1.0.0-*", + "compilationOptions": { + "keyFile": "../../tools/Key.snk" + }, + "description": "Types to model a .NET Project", + "dependencies": { + "System.Reflection.Metadata": "1.2.0-rc2-23911", + "NuGet.Packaging": "3.4.0-rtm-0763", + "Microsoft.Extensions.FileSystemGlobbing": "1.0.0-rc2-15996", + "Microsoft.Extensions.JsonParser.Sources": { + "type": "build", + "version": "1.0.0-rc2-16453" }, - "description": "Types to model a .NET Project", - "dependencies": { - "System.Reflection.Metadata": "1.2.0-rc2-23901", - "NuGet.Packaging": "3.4.0-rtm-0733", - "Microsoft.Extensions.FileSystemGlobbing": "1.0.0-rc2-15996", - "Microsoft.Extensions.JsonParser.Sources": { - "type": "build", - "version": "1.0.0-rc2-16453" - }, - "Microsoft.Extensions.HashCodeCombiner.Sources": { - "type": "build", - "version": "1.0.0-rc2-16054" - }, - "Microsoft.Extensions.DependencyModel": "1.0.0-*" + "Microsoft.Extensions.HashCodeCombiner.Sources": { + "type": "build", + "version": "1.0.0-rc2-16054" }, - "frameworks": { - "net451": { - "frameworkAssemblies": { - "System.Runtime": { - "type": "build" - }, - "System.Collections": { - "type": "build" - }, - "System.IO": { - "type": "build" - } - } + "Microsoft.Extensions.DependencyModel": "1.0.0-*" + }, + "frameworks": { + "net451": { + "frameworkAssemblies": { + "System.Runtime": { + "type": "build" }, - "netstandard1.3": { - "imports": "dnxcore50", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.Dynamic.Runtime": "4.0.11-rc2-23901", - "System.Runtime.Loader": "4.0.0-rc2-23901", - "System.Security.Cryptography.Algorithms": "4.0.0-rc2-23901", - "Microsoft.CSharp": "4.0.1-rc2-23901", - "System.Xml.XDocument": "4.0.11-rc2-23901" - } + "System.Collections": { + "type": "build" + }, + "System.IO": { + "type": "build" } + } + }, + "netstandard1.3": { + "imports": "dnxcore50", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "System.Dynamic.Runtime": "4.0.11-rc2-23911", + "System.Runtime.Loader": "4.0.0-rc2-23911", + "System.Security.Cryptography.Algorithms": "4.0.0-rc2-23911", + "Microsoft.CSharp": "4.0.1-rc2-23911", + "System.Xml.XDocument": "4.0.11-rc2-23911" + } } + } } diff --git a/src/Microsoft.DotNet.TestFramework/Microsoft.DotNet.TestFramework.xproj b/src/Microsoft.DotNet.TestFramework/Microsoft.DotNet.TestFramework.xproj index e6f1e6339..4fc8570fb 100644 --- a/src/Microsoft.DotNet.TestFramework/Microsoft.DotNet.TestFramework.xproj +++ b/src/Microsoft.DotNet.TestFramework/Microsoft.DotNet.TestFramework.xproj @@ -5,7 +5,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 0724ed7c-56e3-4604-9970-25e600611383 Microsoft.DotNet.TestFramework @@ -16,5 +16,5 @@ 2.0 - + diff --git a/src/Microsoft.DotNet.TestFramework/project.json b/src/Microsoft.DotNet.TestFramework/project.json index e04e63cb5..2e096995e 100644 --- a/src/Microsoft.DotNet.TestFramework/project.json +++ b/src/Microsoft.DotNet.TestFramework/project.json @@ -1,4 +1,4 @@ -{ +{ "version": "1.0.0-*", "description": "Microsoft.DotNet.TestFramework Class Library", "authors": [ @@ -11,7 +11,7 @@ "licenseUrl": "", "dependencies": { "Microsoft.DotNet.Cli.Utils": "1.0.0-*", - "NETStandard.Library": "1.0.0-rc2-23901" + "NETStandard.Library": "1.5.0-rc2-23911" }, "frameworks": { "netstandard1.3": { diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContext.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContext.cs index c31ea8dc6..f4f3e34ba 100644 --- a/src/Microsoft.Extensions.DependencyModel/DependencyContext.cs +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContext.cs @@ -11,9 +11,6 @@ namespace Microsoft.Extensions.DependencyModel { public class DependencyContext { - private const string DepsJsonExtension = ".deps.json"; - private const string DepsFileExtension = ".deps"; - private static readonly Lazy _defaultContext = new Lazy(LoadDefault); public DependencyContext(string targetFramework, @@ -22,7 +19,7 @@ namespace Microsoft.Extensions.DependencyModel CompilationOptions compilationOptions, IEnumerable compileLibraries, IEnumerable runtimeLibraries, - IEnumerable> runtimeGraph) + IEnumerable runtimeGraph) { if (targetFramework == null) { @@ -32,6 +29,10 @@ namespace Microsoft.Extensions.DependencyModel { throw new ArgumentNullException(nameof(runtime)); } + if (compilationOptions == null) + { + throw new ArgumentNullException(nameof(compilationOptions)); + } if (compileLibraries == null) { throw new ArgumentNullException(nameof(compileLibraries)); @@ -68,48 +69,47 @@ namespace Microsoft.Extensions.DependencyModel public IReadOnlyList RuntimeLibraries { get; } - public IReadOnlyList> RuntimeGraph { get; } + public IReadOnlyList RuntimeGraph { get; } + + public DependencyContext Merge(DependencyContext other) + { + if (other == null) + { + throw new ArgumentNullException(nameof(other)); + } + + return new DependencyContext( + TargetFramework, + Runtime, + IsPortable, + CompilationOptions, + CompileLibraries.Union(other.CompileLibraries, new LibraryMergeEqualityComparer()), + RuntimeLibraries.Union(other.RuntimeLibraries, new LibraryMergeEqualityComparer()), + RuntimeGraph.Union(other.RuntimeGraph) + ); + } private static DependencyContext LoadDefault() { - var entryAssembly = Assembly.GetEntryAssembly(); - return Load(entryAssembly); + return DependencyContextLoader.Default.Load(Assembly.GetEntryAssembly()); } public static DependencyContext Load(Assembly assembly) { - if (assembly == null) + return DependencyContextLoader.Default.Load(assembly); + } + + private class LibraryMergeEqualityComparer: IEqualityComparer where T:Library + { + public bool Equals(T x, T y) { - throw new ArgumentNullException(nameof(assembly)); + return string.Equals(x.Name, y.Name, StringComparison.Ordinal); } - using (var stream = assembly.GetManifestResourceStream(assembly.GetName().Name + DepsJsonExtension)) + public int GetHashCode(T obj) { - if (stream != null) - { - return new DependencyContextJsonReader().Read(stream); - } + return obj.Name.GetHashCode(); } - - var depsJsonFile = Path.ChangeExtension(assembly.Location, DepsJsonExtension); - if (File.Exists(depsJsonFile)) - { - using (var stream = File.OpenRead(depsJsonFile)) - { - return new DependencyContextJsonReader().Read(stream); - } - } - - var depsFile = Path.ChangeExtension(assembly.Location, DepsFileExtension); - if (File.Exists(depsFile)) - { - using (var stream = File.OpenRead(depsFile)) - { - return new DependencyContextCsvReader().Read(stream); - } - } - - return null; } } } diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContextCsvReader.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContextCsvReader.cs index ebab91f2e..0e1e5e54a 100644 --- a/src/Microsoft.Extensions.DependencyModel/DependencyContextCsvReader.cs +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContextCsvReader.cs @@ -9,7 +9,7 @@ using System.Text; namespace Microsoft.Extensions.DependencyModel { - public class DependencyContextCsvReader + public class DependencyContextCsvReader: IDependencyContextReader { public DependencyContext Read(Stream stream) { @@ -62,7 +62,7 @@ namespace Microsoft.Extensions.DependencyModel compilationOptions: CompilationOptions.Default, compileLibraries: Enumerable.Empty(), runtimeLibraries: runtimeLibraries.ToArray(), - runtimeGraph: Enumerable.Empty>()); + runtimeGraph: Enumerable.Empty()); } private Tuple PackageIdentity(DepsFileLine line) diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs index ca0d90916..12019b06f 100644 --- a/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs @@ -10,7 +10,7 @@ using Newtonsoft.Json.Linq; namespace Microsoft.Extensions.DependencyModel { - public class DependencyContextJsonReader + public class DependencyContextJsonReader: IDependencyContextReader { public DependencyContext Read(Stream stream) { @@ -79,7 +79,7 @@ namespace Microsoft.Extensions.DependencyModel ); } - private IEnumerable> ReadRuntimeGraph(JObject runtimes) + private IEnumerable ReadRuntimeGraph(JObject runtimes) { if (runtimes == null) { @@ -90,7 +90,7 @@ namespace Microsoft.Extensions.DependencyModel var runtime = (JProperty)targets.Single(); foreach (var pair in (JObject)runtime.Value) { - yield return new KeyValuePair(pair.Key, pair.Value.Values().ToArray()); + yield return new RuntimeFallbacks(pair.Key, pair.Value.Values().ToArray()); } } diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContextLoader.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContextLoader.cs new file mode 100644 index 000000000..4a4256bb1 --- /dev/null +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContextLoader.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.Extensions.EnvironmentAbstractions; + +namespace Microsoft.Extensions.DependencyModel +{ + public class DependencyContextLoader + { + private static Lazy _depsFiles = new Lazy(GetHostDepsList); + + private const string DepsJsonExtension = ".deps.json"; + private const string DepsExtension = ".deps"; + + private readonly string _entryPointDepsLocation; + private readonly string _runtimeDepsLocation; + private readonly IFileSystem _fileSystem; + private readonly IDependencyContextReader _jsonReader; + private readonly IDependencyContextReader _csvReader; + + public DependencyContextLoader() : this( + GetDefaultEntrypointDepsLocation(), + GetDefaultRuntimeDepsLocation(), + FileSystemWrapper.Default, + new DependencyContextJsonReader(), + new DependencyContextCsvReader()) + { + } + + internal DependencyContextLoader( + string entryPointDepsLocation, + string runtimeDepsLocation, + IFileSystem fileSystem, + IDependencyContextReader jsonReader, + IDependencyContextReader csvReader) + { + _entryPointDepsLocation = entryPointDepsLocation; + _runtimeDepsLocation = runtimeDepsLocation; + _fileSystem = fileSystem; + _jsonReader = jsonReader; + _csvReader = csvReader; + } + + public static DependencyContextLoader Default { get; } = new DependencyContextLoader(); + + internal virtual bool IsEntryAssembly(Assembly assembly) + { + return assembly.GetName() == Assembly.GetEntryAssembly().GetName(); + } + + internal virtual Stream GetResourceStream(Assembly assembly, string name) + { + return assembly.GetManifestResourceStream(name); + } + + public DependencyContext Load(Assembly assembly) + { + if (assembly == null) + { + throw new ArgumentNullException(nameof(assembly)); + } + + DependencyContext context = null; + + if (IsEntryAssembly(assembly)) + { + context = LoadEntryAssemblyContext(); + } + + if (context == null) + { + context = LoadAssemblyContext(assembly); + } + + if (context?.IsPortable == true) + { + var runtimeContext = LoadRuntimeContext(); + if (runtimeContext != null) + { + context = context.Merge(runtimeContext); + } + } + return context; + } + + private DependencyContext LoadEntryAssemblyContext() + { + if (!string.IsNullOrEmpty(_entryPointDepsLocation)) + { + Debug.Assert(File.Exists(_entryPointDepsLocation)); + using (var stream = _fileSystem.File.OpenRead(_entryPointDepsLocation)) + { + return _jsonReader.Read(stream); + } + } + return null; + } + + private DependencyContext LoadRuntimeContext() + { + if (!string.IsNullOrEmpty(_runtimeDepsLocation)) + { + Debug.Assert(File.Exists(_runtimeDepsLocation)); + using (var stream = _fileSystem.File.OpenRead(_runtimeDepsLocation)) + { + return _jsonReader.Read(stream); + } + } + return null; + } + + private DependencyContext LoadAssemblyContext(Assembly assembly) + { + using (var stream = GetResourceStream(assembly, assembly.GetName().Name + DepsJsonExtension)) + { + if (stream != null) + { + return _jsonReader.Read(stream); + } + } + + var depsJsonFile = Path.ChangeExtension(assembly.Location, DepsJsonExtension); + if (_fileSystem.File.Exists(depsJsonFile)) + { + using (var stream = _fileSystem.File.OpenRead(depsJsonFile)) + { + return _jsonReader.Read(stream); + } + } + + var depsFile = Path.ChangeExtension(assembly.Location, DepsExtension); + if (_fileSystem.File.Exists(depsFile)) + { + using (var stream = _fileSystem.File.OpenRead(depsFile)) + { + return _csvReader.Read(stream); + } + } + + return null; + } + + private static string GetDefaultRuntimeDepsLocation() + { + var deps = _depsFiles.Value; + if (deps != null && deps.Length > 1) + { + return deps[1]; + } + return null; + } + + private static string GetDefaultEntrypointDepsLocation() + { + var deps = _depsFiles.Value; + if (deps != null && deps.Length > 0) + { + return deps[0]; + } + return null; + } + + private static string[] GetHostDepsList() + { + // TODO: Were going to replace this with AppContext.GetData + var appDomainType = typeof(object).GetTypeInfo().Assembly?.GetType("System.AppDomain"); + var currentDomain = appDomainType?.GetProperty("CurrentDomain")?.GetValue(null); + var deps = appDomainType?.GetMethod("GetData")?.Invoke(currentDomain, new[] { "APP_CONTEXT_DEPS_FILES" }); + + return (deps as string)?.Split(new [] { ';' }, StringSplitOptions.RemoveEmptyEntries); + } + + } +} diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContextWriter.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContextWriter.cs index 1a6235345..ecc06a77b 100644 --- a/src/Microsoft.Extensions.DependencyModel/DependencyContextWriter.cs +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContextWriter.cs @@ -50,7 +50,7 @@ namespace Microsoft.Extensions.DependencyModel private JObject WriteRuntimeGraph(DependencyContext context) { return new JObject( - context.RuntimeGraph.Select(g => new JProperty(g.Key, new JArray(g.Value))) + context.RuntimeGraph.Select(g => new JProperty(g.Runtime, new JArray(g.Fallbacks))) ); } @@ -79,7 +79,7 @@ namespace Microsoft.Extensions.DependencyModel { if (value != null) { - o[name] = value.ToString(); + o.Add(new JProperty(name, value)); } } @@ -168,7 +168,7 @@ namespace Microsoft.Extensions.DependencyModel { return; } - libraryObject.Add( + libraryObject.AddFirst( new JProperty(DependencyContextStrings.DependenciesPropertyName, new JObject( dependencies.Select(dependency => new JProperty(dependency.Name, dependency.Version)))) @@ -232,8 +232,8 @@ namespace Microsoft.Extensions.DependencyModel new JObject(runtimeLibrary.RuntimeTargets.SelectMany(WriteRuntimeTarget))) ); } - AddResourceAssemblies(libraryObject, runtimeLibrary.ResourceAssemblies); AddRuntimeAssemblies(libraryObject, runtimeLibrary.Assemblies); + AddResourceAssemblies(libraryObject, runtimeLibrary.ResourceAssemblies); libraryObject.Add(DependencyContextStrings.NativeLibrariesKey, WriteAssetList(runtimeLibrary.NativeLibraries)); dependencies.UnionWith(runtimeLibrary.Dependencies); diff --git a/src/Microsoft.Extensions.DependencyModel/IDependencyContextReader.cs b/src/Microsoft.Extensions.DependencyModel/IDependencyContextReader.cs new file mode 100644 index 000000000..57b2a442e --- /dev/null +++ b/src/Microsoft.Extensions.DependencyModel/IDependencyContextReader.cs @@ -0,0 +1,9 @@ +using System.IO; + +namespace Microsoft.Extensions.DependencyModel +{ + public interface IDependencyContextReader + { + DependencyContext Read(Stream stream); + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.DependencyModel/Microsoft.Extensions.DependencyModel.xproj b/src/Microsoft.Extensions.DependencyModel/Microsoft.Extensions.DependencyModel.xproj index 8c6e7884f..8e2d9aca8 100644 --- a/src/Microsoft.Extensions.DependencyModel/Microsoft.Extensions.DependencyModel.xproj +++ b/src/Microsoft.Extensions.DependencyModel/Microsoft.Extensions.DependencyModel.xproj @@ -4,7 +4,7 @@ 14.0.23107 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 688870c8-9843-4f9e-8576-d39290ad0f25 Microsoft.Extensions.DependencyModel @@ -14,5 +14,5 @@ 2.0 - + \ No newline at end of file diff --git a/src/Microsoft.Extensions.DependencyModel/RuntimeFallbacks.cs b/src/Microsoft.Extensions.DependencyModel/RuntimeFallbacks.cs new file mode 100644 index 000000000..d759b1942 --- /dev/null +++ b/src/Microsoft.Extensions.DependencyModel/RuntimeFallbacks.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; + +namespace Microsoft.Extensions.DependencyModel +{ + public class RuntimeFallbacks + { + public string Runtime { get; set; } + public IEnumerable Fallbacks { get; set; } + + public RuntimeFallbacks(string runtime, IEnumerable fallbacks) + { + Runtime = runtime; + Fallbacks = fallbacks; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.DependencyModel/project.json b/src/Microsoft.Extensions.DependencyModel/project.json index 1ad395b56..65fd21652 100644 --- a/src/Microsoft.Extensions.DependencyModel/project.json +++ b/src/Microsoft.Extensions.DependencyModel/project.json @@ -1,39 +1,38 @@ { - "description": "Abstractions for reading `.deps` files.", - "version": "1.0.0-*", - "repository": { - "type": "git", - "url": "git://github.com/dotnet/cli" + "description": "Abstractions for reading `.deps` files.", + "version": "1.0.0-*", + "repository": { + "type": "git", + "url": "git://github.com/dotnet/cli" + }, + "compilationOptions": { + "warningsAsErrors": true, + "keyFile": "../../tools/Key.snk" + }, + "dependencies": { + "Microsoft.Extensions.HashCodeCombiner.Sources": { + "type": "build", + "version": "1.0.0-rc2-16054" }, - "compilationOptions": { - "warningsAsErrors": true, - "keyFile": "../../tools/Key.snk" + "Microsoft.DotNet.InternalAbstractions": { + "target": "project", + "version": "1.0.0-*" }, - "dependencies": { - "Microsoft.Extensions.HashCodeCombiner.Sources": { - "type": "build", - "version": "1.0.0-rc2-16054" - }, - "Microsoft.DotNet.InternalAbstractions": { - "target": "project", - "version": "1.0.0-*" - }, - - "Newtonsoft.Json": "7.0.1", - "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537" - }, - "frameworks": { - "net451": {}, - "netstandard1.3": { - "imports": "dnxcore50", - "dependencies": { - "System.IO.FileSystem": "4.0.1-rc2-23901", - "System.Linq": "4.0.1-rc2-23901", - "System.Runtime": "4.0.21-rc2-23901", - "System.Reflection": "4.1.0-rc2-23901", - "System.Dynamic.Runtime": "4.0.11-rc2-23901" - } - } - }, - "scripts": {} -} \ No newline at end of file + "Newtonsoft.Json": "7.0.1", + "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537" + }, + "frameworks": { + "net451": {}, + "netstandard1.3": { + "imports": "dnxcore50", + "dependencies": { + "System.IO.FileSystem": "4.0.1-rc2-23911", + "System.Linq": "4.0.1-rc2-23911", + "System.Runtime": "4.0.21-rc2-23911", + "System.Reflection": "4.1.0-rc2-23911", + "System.Dynamic.Runtime": "4.0.11-rc2-23911" + } + } + }, + "scripts": {} +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/Microsoft.Extensions.Testing.Abstractions.xproj b/src/Microsoft.Extensions.Testing.Abstractions/Microsoft.Extensions.Testing.Abstractions.xproj index a364b40ec..9d149a5c3 100644 --- a/src/Microsoft.Extensions.Testing.Abstractions/Microsoft.Extensions.Testing.Abstractions.xproj +++ b/src/Microsoft.Extensions.Testing.Abstractions/Microsoft.Extensions.Testing.Abstractions.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + dcdfe282-03de-4dbc-b90c-cc3ce3ec8162 Microsoft.Extensions.Testing.Abstractions @@ -14,5 +14,5 @@ 2.0 - + \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/project.json b/src/Microsoft.Extensions.Testing.Abstractions/project.json index 7ecb6232e..e392da6d6 100644 --- a/src/Microsoft.Extensions.Testing.Abstractions/project.json +++ b/src/Microsoft.Extensions.Testing.Abstractions/project.json @@ -1,29 +1,29 @@ { - "description": "Abstractions for test runners to communicate to a tool, such as Visual Studio.", - "version": "1.0.0-*", - "repository": { - "type": "git", - "url": "git://github.com/dotnet/cli" - }, - "compilationOptions": { - "warningsAsErrors": true, - "keyFile": "../../tools/Key.snk" - }, - "dependencies": { - "Newtonsoft.Json": "7.0.1", - "Microsoft.DotNet.ProjectModel": "1.0.0-*", - "Microsoft.Extensions.Logging.Abstractions": "1.0.0-rc2-16040" - }, - "frameworks": { - "net451": {}, - "netstandard1.3": { - "imports": "dnxcore50", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.Resources.ResourceManager": "4.0.1-rc2-23901", - "System.Runtime.Serialization.Primitives": "4.1.0-rc2-23901" - } - } - }, - "scripts": {} -} \ No newline at end of file + "description": "Abstractions for test runners to communicate to a tool, such as Visual Studio.", + "version": "1.0.0-*", + "repository": { + "type": "git", + "url": "git://github.com/dotnet/cli" + }, + "compilationOptions": { + "warningsAsErrors": true, + "keyFile": "../../tools/Key.snk" + }, + "dependencies": { + "Newtonsoft.Json": "7.0.1", + "Microsoft.DotNet.ProjectModel": "1.0.0-*", + "Microsoft.Extensions.Logging.Abstractions": "1.0.0-rc2-16040" + }, + "frameworks": { + "net451": {}, + "netstandard1.3": { + "imports": "dnxcore50", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "System.Resources.ResourceManager": "4.0.1-rc2-23911", + "System.Reflection.TypeExtensions": "4.1.0-rc2-23911" + } + } + }, + "scripts": {} +} diff --git a/src/corehost/build.sh b/src/corehost/build.sh index 339838348..7717bdc15 100755 --- a/src/corehost/build.sh +++ b/src/corehost/build.sh @@ -14,6 +14,46 @@ while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symli done DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" +__build_arch= +__runtime_id= + +while [ "$1" != "" ]; do + lowerI="$(echo $1 | awk '{print tolower($0)}')" + case $lowerI in + -h|--help) + usage + exit 1 + ;; + --arch) + shift + __build_arch=$1 + ;; + --rid) + shift + __runtime_id=$1 + ;; + *) + echo "Unknown argument to build.sh $1"; exit 1 + esac + shift +done + +__cmake_defines= + +case $__build_arch in + amd64) + __define=-DCLI_CMAKE_PLATFORM_ARCH_AMD64=1 + ;; + x86) + __define=-DCLI_CMAKE_PLATFORM_ARCH_I386=1 + ;; + *) + echo "Unknown architecture $__build_arch"; exit 1 + ;; +esac +__cmake_defines="${__cmake_defines} ${__define}" + + echo "Building Corehost from $DIR to $(pwd)" -cmake "$DIR" -G "Unix Makefiles" +cmake "$DIR" -G "Unix Makefiles" $__cmake_defines -DCLI_CMAKE_RUNTIME_ID:STRING=$__runtime_id make diff --git a/src/corehost/cli/CMakeLists.txt b/src/corehost/cli/CMakeLists.txt index 9c22fc834..12636ae6b 100644 --- a/src/corehost/cli/CMakeLists.txt +++ b/src/corehost/cli/CMakeLists.txt @@ -14,13 +14,26 @@ include(setup.cmake) set (CMAKE_CXX_STANDARD 11) +include_directories(..) include_directories(../common) include_directories(.) +include_directories(./fxr) +include_directories(./json/casablanca/include) # CMake does not recommend using globbing since it messes with the freshness checks set(SOURCES - ../corehost.cpp + libhost.cpp + #deps_format.cpp + #./json/casablanca/src/json/json.cpp + #./json/casablanca/src/json/json_parsing.cpp + #./json/casablanca/src/json/json_serialization.cpp + #./json/casablanca/src/utilities/asyncrt_utils.cpp + + + ./fxr/fx_ver.cpp + ../corehost.cpp + ../policy_load.cpp ../common/trace.cpp ../common/utils.cpp) @@ -32,6 +45,9 @@ else() endif() add_executable(corehost ${SOURCES}) +install(TARGETS corehost DESTINATION bin) +add_definitions(-D_NO_ASYNCRTIMP) +add_definitions(-D_NO_PPLXIMP) # Older CMake doesn't support CMAKE_CXX_STANDARD and GCC/Clang need a switch to enable C++ 11 if(${CMAKE_CXX_COMPILER_ID} MATCHES "(Clang|GNU)") @@ -44,3 +60,4 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") endif() add_subdirectory(dll) +add_subdirectory(fxr) diff --git a/src/corehost/cli/args.cpp b/src/corehost/cli/args.cpp index 086d05fab..569110857 100644 --- a/src/corehost/cli/args.cpp +++ b/src/corehost/cli/args.cpp @@ -4,6 +4,7 @@ #include "args.h" #include "utils.h" #include "coreclr.h" +#include "libhost.h" arguments_t::arguments_t() : managed_application(_X("")), @@ -11,11 +12,8 @@ arguments_t::arguments_t() : app_dir(_X("")), app_argc(0), app_argv(nullptr), - nuget_packages(_X("")), dotnet_packages_cache(_X("")), dotnet_servicing(_X("")), - dotnet_runtime_servicing(_X("")), - dotnet_home(_X("")), deps_path(_X("")) { } @@ -26,12 +24,13 @@ void display_help() _X("Usage: " HOST_EXE_NAME " [ASSEMBLY] [ARGUMENTS]\n") _X("Execute the specified managed assembly with the passed in arguments\n\n") _X("The Host's behavior can be altered using the following environment variables:\n") - _X(" DOTNET_HOME Set the dotnet home directory. The CLR is expected to be in the runtime subdirectory of this directory. Overrides all other values for CLR search paths\n") _X(" COREHOST_TRACE Set to affect trace levels (0 = Errors only (default), 1 = Warnings, 2 = Info, 3 = Verbose)\n"); } -bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& args) +bool parse_arguments(const pal::string_t& deps_path, const pal::string_t& probe_dir, host_mode_t mode, + const int argc, const pal::char_t* argv[], arguments_t* arg_out) { + arguments_t& args = *arg_out; // Get the full name of the application if (!pal::get_own_executable_path(&args.own_path) || !pal::realpath(&args.own_path)) { @@ -41,8 +40,8 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg auto own_name = get_filename(args.own_path); auto own_dir = get_directory(args.own_path); - - if (own_name.compare(HOST_EXE_NAME) == 0) + + if (mode != host_mode_t::standalone) { // corerun mode. First argument is managed app if (argc < 2) @@ -78,23 +77,26 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg args.app_argc = argc - 1; } - if (args.app_argc > 0) + std::unordered_map opts; + std::vector known_opts = { _X("--depsfile"), _X("--additionalprobingpath") }; + int num_args = 0; + if (!parse_known_args(args.app_argc, args.app_argv, known_opts, &opts, &num_args)) { - auto depsfile_candidate = pal::string_t(args.app_argv[0]); - - if (starts_with(depsfile_candidate, s_deps_arg_prefix, false)) - { - args.deps_path = depsfile_candidate.substr(s_deps_arg_prefix.length()); - if (!pal::realpath(&args.deps_path)) - { - trace::error(_X("Failed to locate deps file: %s"), args.deps_path.c_str()); - return false; - } - args.app_dir = get_directory(args.deps_path); - args.app_argc = args.app_argc - 1; - args.app_argv = &args.app_argv[1]; - } + return false; } + + args.app_argc -= num_args; + args.app_argv += num_args; + pal::string_t deps_file = opts.count(_X("--depsfile")) ? opts[_X("--depsfile")] : deps_path; + pal::string_t probe_path = opts.count(_X("--additionalprobingpath")) ? opts[_X("--additionalprobingpath")] : probe_dir; + + if (!deps_file.empty()) + { + args.deps_path = deps_file; + args.app_dir = get_directory(args.deps_path); + } + + args.probe_dir = probe_path; if (args.deps_path.empty()) { @@ -105,13 +107,10 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg args.deps_path.append(app_base); args.deps_path.push_back(DIR_SEPARATOR); args.deps_path.append(app_name, 0, app_name.find_last_of(_X("."))); - args.deps_path.append(_X(".deps")); + args.deps_path.append(_X(".deps.json")); } - pal::getenv(_X("NUGET_PACKAGES"), &args.nuget_packages); pal::getenv(_X("DOTNET_PACKAGES_CACHE"), &args.dotnet_packages_cache); pal::getenv(_X("DOTNET_SERVICING"), &args.dotnet_servicing); - pal::getenv(_X("DOTNET_RUNTIME_SERVICING"), &args.dotnet_runtime_servicing); - pal::getenv(_X("DOTNET_HOME"), &args.dotnet_home); return true; } diff --git a/src/corehost/cli/args.h b/src/corehost/cli/args.h index d510cd6e8..bec56128c 100644 --- a/src/corehost/cli/args.h +++ b/src/corehost/cli/args.h @@ -7,8 +7,7 @@ #include "utils.h" #include "pal.h" #include "trace.h" - -static const pal::string_t s_deps_arg_prefix = _X("--depsfile:"); +#include "libhost.h" struct arguments_t { @@ -18,7 +17,7 @@ struct arguments_t pal::string_t dotnet_servicing; pal::string_t dotnet_runtime_servicing; pal::string_t dotnet_home; - pal::string_t nuget_packages; + pal::string_t probe_dir; pal::string_t dotnet_packages_cache; pal::string_t managed_application; @@ -28,6 +27,6 @@ struct arguments_t arguments_t(); }; -bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& args); +bool parse_arguments(const pal::string_t& deps_path, const pal::string_t& probe_dir, host_mode_t mode, const int argc, const pal::char_t* argv[], arguments_t* args); #endif // ARGS_H diff --git a/src/corehost/cli/deps_entry.cpp b/src/corehost/cli/deps_entry.cpp new file mode 100644 index 000000000..ff5ebbbe3 --- /dev/null +++ b/src/corehost/cli/deps_entry.cpp @@ -0,0 +1,144 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "pal.h" +#include "utils.h" +#include "deps_entry.h" +#include "trace.h" + +// ----------------------------------------------------------------------------- +// Given a "base" directory, yield the relative path of this file in the package +// layout. +// +// Parameters: +// base - The base directory to look for the relative path of this entry +// str - If the method returns true, contains the file path for this deps +// entry relative to the "base" directory +// +// Returns: +// If the file exists in the path relative to the "base" directory. +// +bool deps_entry_t::to_full_path(const pal::string_t& base, pal::string_t* str) const +{ + pal::string_t& candidate = *str; + + candidate.clear(); + + // Base directory must be present to obtain full path + if (base.empty()) + { + return false; + } + + // Entry relative path contains '/' separator, sanitize it to use + // platform separator. Perf: avoid extra copy if it matters. + pal::string_t pal_relative_path = relative_path; + if (_X('/') != DIR_SEPARATOR) + { + replace_char(&pal_relative_path, _X('/'), DIR_SEPARATOR); + } + + // Reserve space for the path below + candidate.reserve(base.length() + + library_name.length() + + library_version.length() + + pal_relative_path.length() + 3); + + candidate.assign(base); + append_path(&candidate, library_name.c_str()); + append_path(&candidate, library_version.c_str()); + append_path(&candidate, pal_relative_path.c_str()); + + bool exists = pal::file_exists(candidate); + if (!exists) + { + candidate.clear(); + } + return exists; +} + +// ----------------------------------------------------------------------------- +// Given a "base" directory, yield the relative path of this file in the package +// layout if the entry hash matches the hash file in the "base" directory +// +// Parameters: +// base - The base directory to look for the relative path of this entry and +// the hash file. +// str - If the method returns true, contains the file path for this deps +// entry relative to the "base" directory +// +// Description: +// Looks for a file named "{PackageName}.{PackageVersion}.nupkg.{HashAlgorithm}" +// If the deps entry's {HashAlgorithm}-{HashValue} matches the contents then +// yields the relative path of this entry in the "base" dir. +// +// Returns: +// If the file exists in the path relative to the "base" directory and there +// was hash file match with this deps entry. +// +// See: to_full_path(base, str) +// +bool deps_entry_t::to_hash_matched_path(const pal::string_t& base, pal::string_t* str) const +{ + pal::string_t& candidate = *str; + + candidate.clear(); + + // Base directory must be present to perform hash lookup. + if (base.empty()) + { + return false; + } + + // First detect position of hyphen in [Algorithm]-[Hash] in the string. + size_t pos = library_hash.find(_X("-")); + if (pos == 0 || pos == pal::string_t::npos) + { + trace::verbose(_X("Invalid hash %s value for deps file entry: %s"), library_hash.c_str(), library_name.c_str()); + return false; + } + + // Build the nupkg file name. Just reserve approx 8 char_t's for the algorithm name. + pal::string_t nupkg_filename; + nupkg_filename.reserve(library_name.length() + 1 + library_version.length() + 16); + nupkg_filename.append(library_name); + nupkg_filename.append(_X(".")); + nupkg_filename.append(library_version); + nupkg_filename.append(_X(".nupkg.")); + nupkg_filename.append(library_hash.substr(0, pos)); + + // Build the hash file path str. + pal::string_t hash_file; + hash_file.reserve(base.length() + library_name.length() + library_version.length() + nupkg_filename.length() + 3); + hash_file.assign(base); + append_path(&hash_file, library_name.c_str()); + append_path(&hash_file, library_version.c_str()); + append_path(&hash_file, nupkg_filename.c_str()); + + // Read the contents of the hash file. + pal::ifstream_t fstream(hash_file); + if (!fstream.good()) + { + trace::verbose(_X("The hash file is invalid [%s]"), hash_file.c_str()); + return false; + } + + // Obtain the hash from the file. + std::string hash; + hash.assign(pal::istreambuf_iterator_t(fstream), + pal::istreambuf_iterator_t()); + pal::string_t pal_hash; + pal::to_palstring(hash.c_str(), &pal_hash); + + // Check if contents match deps entry. + pal::string_t entry_hash = library_hash.substr(pos + 1); + if (entry_hash != pal_hash) + { + trace::verbose(_X("The file hash [%s][%d] did not match entry hash [%s][%d]"), + pal_hash.c_str(), pal_hash.length(), entry_hash.c_str(), entry_hash.length()); + return false; + } + + // All good, just append the relative dir to base. + return to_full_path(base, &candidate); +} diff --git a/src/corehost/cli/deps_entry.h b/src/corehost/cli/deps_entry.h new file mode 100644 index 000000000..81e30e541 --- /dev/null +++ b/src/corehost/cli/deps_entry.h @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef __DEPS_ENTRY_H_ +#define __DEPS_ENTRY_H_ + +#include +#include +#include "pal.h" + +struct deps_entry_t +{ + enum asset_types + { + runtime = 0, + resources, + native, + count + }; + + pal::string_t library_type; + pal::string_t library_name; + pal::string_t library_version; + pal::string_t library_hash; + pal::string_t asset_type; + pal::string_t asset_name; + pal::string_t relative_path; + bool is_serviceable; + + // Given a "base" dir, yield the relative path in the package layout. + bool to_full_path(const pal::string_t& root, pal::string_t* str) const; + + // Given a "base" dir, yield the relative path in the package layout only if + // the hash matches contents of the hash file. + bool to_hash_matched_path(const pal::string_t& root, pal::string_t* str) const; +}; + +#endif // __DEPS_ENTRY_H_ diff --git a/src/corehost/cli/deps_format.cpp b/src/corehost/cli/deps_format.cpp new file mode 100644 index 000000000..c85db98b5 --- /dev/null +++ b/src/corehost/cli/deps_format.cpp @@ -0,0 +1,305 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "deps_format.h" +#include "utils.h" +#include "trace.h" +#include +#include +#include +#include +#include +#include + +const std::array deps_json_t::s_known_asset_types = { + _X("runtime"), _X("resources"), _X("native") }; + +const deps_entry_t& deps_json_t::try_ni(const deps_entry_t& entry) const +{ + if (m_ni_entries.count(entry.asset_name)) + { + int index = m_ni_entries.at(entry.asset_name); + return m_deps_entries[deps_entry_t::asset_types::runtime][index]; + } + return entry; +} + +void deps_json_t::reconcile_libraries_with_targets( + const json_value& json, + const std::function& library_exists_fn, + const std::function&(const pal::string_t&, int)>& get_rel_paths_by_asset_type_fn) +{ + const auto& libraries = json.at(_X("libraries")).as_object(); + for (const auto& library : libraries) + { + trace::info(_X("Reconciling library %s"), library.first.c_str()); + + if (pal::to_lower(library.second.at(_X("type")).as_string()) != _X("package")) + { + trace::info(_X("Library %s is not a package"), library.first.c_str()); + continue; + } + if (!library_exists_fn(library.first)) + { + trace::info(_X("Library %s does not exist"), library.first.c_str()); + continue; + } + + const auto& properties = library.second.as_object(); + + const pal::string_t& hash = properties.at(_X("sha512")).as_string(); + bool serviceable = properties.at(_X("serviceable")).as_bool(); + + for (int i = 0; i < s_known_asset_types.size(); ++i) + { + for (const auto& rel_path : get_rel_paths_by_asset_type_fn(library.first, i)) + { + bool ni_dll = false; + auto asset_name = get_filename_without_ext(rel_path); + if (ends_with(asset_name, _X(".ni"), false)) + { + ni_dll = true; + asset_name = strip_file_ext(asset_name); + } + + deps_entry_t entry; + size_t pos = library.first.find(_X("/")); + entry.library_name = library.first.substr(0, pos); + entry.library_version = library.first.substr(pos + 1); + entry.library_type = _X("package"); + entry.library_hash = hash; + entry.asset_name = asset_name; + entry.asset_type = s_known_asset_types[i]; + entry.relative_path = rel_path; + entry.is_serviceable = serviceable; + + // TODO: Deps file does not follow spec. It uses '\\', should use '/' + replace_char(&entry.relative_path, _X('\\'), _X('/')); + + m_deps_entries[i].push_back(entry); + + if (ni_dll) + { + m_ni_entries[entry.asset_name] = m_deps_entries + [deps_entry_t::asset_types::runtime].size() - 1; + } + + trace::info(_X("Added %s %s deps entry [%d] [%s, %s, %s]"), s_known_asset_types[i], entry.asset_name.c_str(), m_deps_entries[i].size() - 1, entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); + + if (i == deps_entry_t::asset_types::native && + entry.asset_name == LIBCORECLR_FILENAME) + { + m_coreclr_index = m_deps_entries[i].size() - 1; + trace::verbose(_X("Found coreclr from deps %d [%s, %s, %s]"), + m_coreclr_index, + entry.library_name.c_str(), + entry.library_version.c_str(), + entry.relative_path.c_str()); + } + + } + } + } +} + +pal::string_t get_own_rid() +{ +#define _STRINGIFY(s) _X(s) +#if defined(TARGET_RUNTIME_ID) + return _STRINGIFY(TARGET_RUNTIME_ID); +#else +#error "Cannot build the host without knowing host's root RID" +#endif +} + +bool deps_json_t::perform_rid_fallback(rid_specific_assets_t* portable_assets, const rid_fallback_graph_t& rid_fallback_graph) +{ + pal::string_t host_rid = get_own_rid(); + for (auto& package : *portable_assets) + { + pal::string_t matched_rid = package.second.count(host_rid) ? host_rid : _X(""); + if (matched_rid.empty()) + { + if (rid_fallback_graph.count(host_rid) == 0) + { + trace::error(_X("Did not find fallback rids for package %s for the host rid %s"), package.first.c_str(), host_rid.c_str()); + return false; + } + const auto& fallback_rids = rid_fallback_graph.find(host_rid)->second; + auto iter = std::find_if(fallback_rids.begin(), fallback_rids.end(), [&package](const pal::string_t& rid) { + return package.second.count(rid); + }); + if (iter == fallback_rids.end() || (*iter).empty()) + { + trace::error(_X("Did not find a matching fallback rid for package %s for the host rid %s"), package.first.c_str(), host_rid.c_str()); + return false; + } + matched_rid = *iter; + } + assert(!matched_rid.empty()); + for (auto iter = package.second.begin(); iter != package.second.end(); /* */) + { + iter = (iter->first != matched_rid) + ? package.second.erase(iter) + : iter++; + } + } + return true; +} + + +bool deps_json_t::process_runtime_targets(const json_value& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph, rid_specific_assets_t* p_assets) +{ + rid_specific_assets_t& assets = *p_assets; + for (const auto& package : json.at(_X("targets")).at(target_name).as_object()) + { + const auto& targets = package.second.as_object(); + auto iter = targets.find(_X("runtimeTargets")); + if (iter == targets.end()) + { + continue; + } + + const auto& files = iter->second.as_object(); + for (const auto& file : files) + { + const auto& type = file.second.at(_X("assetType")).as_string(); + for (int i = 0; i < s_known_asset_types.size(); ++i) + { + if (pal::strcasecmp(type.c_str(), s_known_asset_types[i]) == 0) + { + const auto& rid = file.second.at(_X("rid")).as_string(); + assets[package.first][rid][i].push_back(file.first); + } + } + } + } + + if (!perform_rid_fallback(&assets, rid_fallback_graph)) + { + return false; + } + + return true; +} + +bool deps_json_t::process_targets(const json_value& json, const pal::string_t& target_name, deps_assets_t* p_assets) +{ + deps_assets_t& assets = *p_assets; + for (const auto& package : json.at(_X("targets")).at(target_name).as_object()) + { + // if (package.second.at(_X("type")).as_string() != _X("package")) continue; + + const auto& asset_types = package.second.as_object(); + for (int i = 0; i < s_known_asset_types.size(); ++i) + { + auto iter = asset_types.find(s_known_asset_types[i]); + if (iter != asset_types.end()) + { + for (const auto& file : iter->second.as_object()) + { + trace::info(_X("Adding %s asset %s from %s"), s_known_asset_types[i], file.first.c_str(), package.first.c_str()); + assets[package.first][i].push_back(file.first); + } + } + } + } + return true; +} + +bool deps_json_t::load_portable(const json_value& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph) +{ + rid_specific_assets_t rid_assets; + if (!process_runtime_targets(json, target_name, rid_fallback_graph, &rid_assets)) + { + return false; + } + + deps_assets_t non_rid_assets; + if (!process_targets(json, target_name, &non_rid_assets)) + { + return false; + } + + auto package_exists = [&rid_assets, &non_rid_assets](const pal::string_t& package) -> bool { + return rid_assets.count(package) || non_rid_assets.count(package); + }; + auto get_relpaths = [&rid_assets, &non_rid_assets](const pal::string_t& package, int type_index) -> const std::vector& { + return (rid_assets.count(package)) + ? rid_assets[package].begin()->second[type_index] + : non_rid_assets[package][type_index]; + }; + + reconcile_libraries_with_targets(json, package_exists, get_relpaths); + + return true; +} + +bool deps_json_t::load_standalone(const json_value& json, const pal::string_t& target_name) +{ + deps_assets_t assets; + + if (!process_targets(json, target_name, &assets)) + { + return false; + } + + auto package_exists = [&assets](const pal::string_t& package) -> bool { + return assets.count(package); + }; + + auto get_relpaths = [&assets](const pal::string_t& package, int type_index) -> const std::vector& { + return assets[package][type_index]; + }; + + reconcile_libraries_with_targets(json, package_exists, get_relpaths); + + const auto& json_object = json.as_object(); + const auto iter = json_object.find(_X("runtimes")); + if (iter != json_object.end()) + { + for (const auto& rid : iter->second.as_object()) + { + auto& vec = m_rid_fallback_graph[rid.first]; + for (const auto& fallback : rid.second.as_array()) + { + vec.push_back(fallback.as_string()); + } + } + } + return true; +} + +// ----------------------------------------------------------------------------- +// Load the deps file and parse its "entry" lines which contain the "fields" of +// the entry. Populate an array of these entries. +// +bool deps_json_t::load(bool portable, const pal::string_t& deps_path, const rid_fallback_graph_t& rid_fallback_graph) +{ + // If file doesn't exist, then assume parsed. + if (!pal::file_exists(deps_path)) + { + return true; + } + + // Somehow the file stream could not be opened. This is an error. + pal::ifstream_t file(deps_path); + if (!file.good()) + { + return false; + } + + try + { + const auto json = json_value::parse(file); + + const auto& runtime_target = json.at(_X("runtimeTarget")); + const pal::string_t& name = runtime_target.as_string(); + + return (portable) ? load_portable(json, name, rid_fallback_graph) : load_standalone(json, name); + } + catch (...) + { + return false; + } +} diff --git a/src/corehost/cli/deps_format.h b/src/corehost/cli/deps_format.h new file mode 100644 index 000000000..e9c8a0cea --- /dev/null +++ b/src/corehost/cli/deps_format.h @@ -0,0 +1,113 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef __DEPS_FORMAT_H_ +#define __DEPS_FORMAT_H_ + +#include +#include +#include +#include "pal.h" +#include "deps_entry.h" +#include "cpprest/json.h" + +class deps_json_t +{ + typedef web::json::value json_value; + typedef std::array, deps_entry_t::asset_types::count> vectors_t; + typedef std::unordered_map str_to_vectors_map_t; + typedef std::unordered_map> str_to_vector_map_t; + + typedef str_to_vector_map_t rid_fallback_graph_t; + typedef str_to_vectors_map_t deps_assets_t; + typedef std::unordered_map rid_specific_assets_t; + +public: + deps_json_t() + : m_valid(false) + , m_coreclr_index(-1) + { + } + + deps_json_t(bool portable, const pal::string_t& deps_path) + : deps_json_t(portable, deps_path, m_rid_fallback_graph /* dummy */) + { + } + + deps_json_t(bool portable, const pal::string_t& deps_path, const rid_fallback_graph_t& graph) + : deps_json_t() + { + m_valid = load(portable, deps_path, graph); + } + + const std::vector& get_entries(deps_entry_t::asset_types type) + { + assert(type < deps_entry_t::asset_types::count); + return m_deps_entries[type]; + } + + bool has_coreclr_entry() + { + return m_coreclr_index >= 0; + } + + const deps_entry_t& get_coreclr_entry() + { + assert(has_coreclr_entry()); + return m_deps_entries[deps_entry_t::asset_types::native][m_coreclr_index]; + } + + bool is_valid() + { + return m_valid; + } + + const rid_fallback_graph_t& get_rid_fallback_graph() + { + return m_rid_fallback_graph; + } + + const deps_entry_t& try_ni(const deps_entry_t& entry) const; + +private: + bool load_standalone(const json_value& json, const pal::string_t& target_name); + bool load_portable(const json_value& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph); + bool load(bool portable, const pal::string_t& deps_path, const rid_fallback_graph_t& rid_fallback_graph); + bool process_runtime_targets(const json_value& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph, rid_specific_assets_t* p_assets); + bool process_targets(const json_value& json, const pal::string_t& target_name, deps_assets_t* p_assets); + + void reconcile_libraries_with_targets( + const json_value& json, + const std::function& library_exists_fn, + const std::function&(const pal::string_t&, int)>& get_rel_paths_by_asset_type_fn); + + bool perform_rid_fallback(rid_specific_assets_t* portable_assets, const rid_fallback_graph_t& rid_fallback_graph); + + static const std::array s_known_asset_types; + + std::vector m_deps_entries[deps_entry_t::asset_types::count]; + + std::unordered_map m_ni_entries; + rid_fallback_graph_t m_rid_fallback_graph; + int m_coreclr_index; + bool m_valid; +}; + +class deps_text_t +{ +public: + deps_text_t(const pal::string_t& deps_path) + : m_valid(load(deps_path)) + { + } + + bool load(const pal::string_t& deps_path); + bool is_valid() { return m_valid; } + const std::vector& get_entries() { return m_deps_entries; } + +private: + std::vector m_deps_entries; + bool m_valid; +}; + +#endif // __DEPS_FORMAT_H_ diff --git a/src/corehost/cli/deps_resolver.cpp b/src/corehost/cli/deps_resolver.cpp index a43fdab34..fd2901d8a 100644 --- a/src/corehost/cli/deps_resolver.cpp +++ b/src/corehost/cli/deps_resolver.cpp @@ -6,80 +6,12 @@ #include #include "trace.h" +#include "deps_format.h" #include "deps_resolver.h" #include "utils.h" namespace { -// ----------------------------------------------------------------------------- -// Read a single field from the deps entry -// -// Parameters: -// line - A deps file entry line -// buf - The temporary buffer to use while parsing (with size to contain "line") -// ofs - The offset that this method will read from "line" on invocation and -// the offset that has been consumed by this method upon successful exit -// field - The current field read from the line -// -// Assumption: -// The line should be in a CSV format, with commas separating the fields. -// The fields themselves will be quoted. The escape character is '\\' -// -// Returns: -// True if parsed successfully. Else, false -// -// Note: -// Callers cannot call with the same "line" upon an unsuccessful exit. -bool read_field(const pal::string_t& line, pal::char_t* buf, unsigned* ofs, pal::string_t* field) -{ - unsigned& offset = *ofs; - pal::string_t& value_recv = *field; - - // The first character should be a '"' - if (line[offset] != '"') - { - trace::error(_X("Error reading TPA file")); - return false; - } - offset++; - - auto buf_offset = 0; - - // Iterate through characters in the string - for (; offset < line.length(); offset++) - { - // Is this a '\'? - if (line[offset] == '\\') - { - // Skip this character and read the next character into the buffer - offset++; - buf[buf_offset] = line[offset]; - } - // Is this a '"'? - else if (line[offset] == '\"') - { - // Done! Advance to the pointer after the input - offset++; - break; - } - else - { - // Take the character - buf[buf_offset] = line[offset]; - } - buf_offset++; - } - buf[buf_offset] = '\0'; - value_recv.assign(buf); - - // Consume the ',' if we have one - if (line[offset] == ',') - { - offset++; - } - return true; -} - // ----------------------------------------------------------------------------- // A uniqifying append helper that doesn't let two entries with the same // "asset_name" be part of the "output" paths. @@ -106,28 +38,6 @@ void add_tpa_asset( items->insert(asset_name); } -// ----------------------------------------------------------------------------- -// Add mscorlib from the CLR directory. Even if CLR is serviced, we should pick -// mscorlib from the CLR directory. If mscorlib could not be found in the CLR -// location, then leave it to the CLR to pick the right mscorlib. -// -void add_mscorlib_to_tpa(const pal::string_t& clr_dir, std::set* items, pal::string_t* output) -{ - pal::string_t mscorlib_ni_path = clr_dir + DIR_SEPARATOR + _X("mscorlib.ni.dll"); - if (pal::file_exists(mscorlib_ni_path)) - { - add_tpa_asset(_X("mscorlib"), mscorlib_ni_path, items, output); - return; - } - - pal::string_t mscorlib_path = clr_dir + DIR_SEPARATOR + _X("mscorlib.dll"); - if (pal::file_exists(mscorlib_path)) - { - add_tpa_asset(_X("mscorlib"), mscorlib_path, items, output); - return; - } -} - // ----------------------------------------------------------------------------- // A uniqifying append helper that doesn't let two "paths" to be identical in // the "output" string. @@ -157,234 +67,16 @@ void add_unique_path( } // end of anonymous namespace -// ----------------------------------------------------------------------------- -// Given a "base" directory, yield the relative path of this file in the package -// layout. -// -// Parameters: -// base - The base directory to look for the relative path of this entry -// str - If the method returns true, contains the file path for this deps -// entry relative to the "base" directory -// -// Returns: -// If the file exists in the path relative to the "base" directory. -// -bool deps_entry_t::to_full_path(const pal::string_t& base, pal::string_t* str) const -{ - pal::string_t& candidate = *str; - - candidate.clear(); - - // Entry relative path contains '/' separator, sanitize it to use - // platform separator. Perf: avoid extra copy if it matters. - pal::string_t pal_relative_path = relative_path; - if (_X('/') != DIR_SEPARATOR) - { - replace_char(&pal_relative_path, _X('/'), DIR_SEPARATOR); - } - - // Reserve space for the path below - candidate.reserve(base.length() + - library_name.length() + - library_version.length() + - pal_relative_path.length() + 3); - - candidate.assign(base); - append_path(&candidate, library_name.c_str()); - append_path(&candidate, library_version.c_str()); - append_path(&candidate, pal_relative_path.c_str()); - - bool exists = pal::file_exists(candidate); - if (!exists) - { - candidate.clear(); - } - return exists; -} - -// ----------------------------------------------------------------------------- -// Given a "base" directory, yield the relative path of this file in the package -// layout if the entry hash matches the hash file in the "base" directory -// -// Parameters: -// base - The base directory to look for the relative path of this entry and -// the hash file. -// str - If the method returns true, contains the file path for this deps -// entry relative to the "base" directory -// -// Description: -// Looks for a file named "{PackageName}.{PackageVersion}.nupkg.{HashAlgorithm}" -// If the deps entry's {HashAlgorithm}-{HashValue} matches the contents then -// yields the relative path of this entry in the "base" dir. -// -// Returns: -// If the file exists in the path relative to the "base" directory and there -// was hash file match with this deps entry. -// -// See: to_full_path(base, str) -// -bool deps_entry_t::to_hash_matched_path(const pal::string_t& base, pal::string_t* str) const -{ - pal::string_t& candidate = *str; - - candidate.clear(); - - // Base directory must be present to perform hash lookup. - if (base.empty()) - { - return false; - } - - // First detect position of hyphen in [Algorithm]-[Hash] in the string. - size_t pos = library_hash.find(_X("-")); - if (pos == 0 || pos == pal::string_t::npos) - { - trace::verbose(_X("Invalid hash %s value for deps file entry: %s"), library_hash.c_str(), library_name.c_str()); - return false; - } - - // Build the nupkg file name. Just reserve approx 8 char_t's for the algorithm name. - pal::string_t nupkg_filename; - nupkg_filename.reserve(library_name.length() + 1 + library_version.length() + 16); - nupkg_filename.append(library_name); - nupkg_filename.append(_X(".")); - nupkg_filename.append(library_version); - nupkg_filename.append(_X(".nupkg.")); - nupkg_filename.append(library_hash.substr(0, pos)); - - // Build the hash file path str. - pal::string_t hash_file; - hash_file.reserve(base.length() + library_name.length() + library_version.length() + nupkg_filename.length() + 3); - hash_file.assign(base); - append_path(&hash_file, library_name.c_str()); - append_path(&hash_file, library_version.c_str()); - append_path(&hash_file, nupkg_filename.c_str()); - - // Read the contents of the hash file. - pal::ifstream_t fstream(hash_file); - if (!fstream.good()) - { - trace::verbose(_X("The hash file is invalid [%s]"), hash_file.c_str()); - return false; - } - - // Obtain the hash from the file. - std::string hash; - hash.assign(pal::istreambuf_iterator_t(fstream), - pal::istreambuf_iterator_t()); - pal::string_t pal_hash; - pal::to_palstring(hash.c_str(), &pal_hash); - - // Check if contents match deps entry. - pal::string_t entry_hash = library_hash.substr(pos + 1); - if (entry_hash != pal_hash) - { - trace::verbose(_X("The file hash [%s][%d] did not match entry hash [%s][%d]"), - pal_hash.c_str(), pal_hash.length(), entry_hash.c_str(), entry_hash.length()); - return false; - } - - // All good, just append the relative dir to base. - return to_full_path(base, &candidate); -} - - -// ----------------------------------------------------------------------------- -// Load the deps file and parse its "entry" lines which contain the "fields" of -// the entry. Populate an array of these entries. -// -bool deps_resolver_t::load() -{ - // If file doesn't exist, then assume parsed. - if (!pal::file_exists(m_deps_path)) - { - return true; - } - - // Somehow the file stream could not be opened. This is an error. - pal::ifstream_t file(m_deps_path); - if (!file.good()) - { - return false; - } - - // Parse the "entry" lines of the deps file. - std::string stdline; - while (std::getline(file, stdline)) - { - pal::string_t line; - pal::to_palstring(stdline.c_str(), &line); - - deps_entry_t entry; - pal::string_t is_serviceable; - pal::string_t* fields[] = { - &entry.library_type, - &entry.library_name, - &entry.library_version, - &entry.library_hash, - &entry.asset_type, - &entry.asset_name, - &entry.relative_path, - // TODO: Add when the deps file support is enabled. - // &is_serviceable - }; - - std::vector buf(line.length()); - - for (unsigned i = 0, offset = 0; i < sizeof(fields) / sizeof(fields[0]); ++i) - { - if (!(read_field(line, buf.data(), &offset, fields[i]))) - { - return false; - } - } - - // Serviceable, if not false, default is true. - entry.is_serviceable = pal::strcasecmp(is_serviceable.c_str(), _X("false")) != 0; - - // TODO: Deps file does not follow spec. It uses '\\', should use '/' - replace_char(&entry.relative_path, _X('\\'), _X('/')); - - m_deps_entries.push_back(entry); - - trace::verbose(_X("Added deps entry [%d] [%s, %s, %s]"), m_deps_entries.size() - 1, entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str()); - - static_assert(std::is_same, decltype(m_deps_entries)>::value, "decltype(m_deps_entries) not a vector, took index based on size."); - if (entry.asset_type == _X("native") && - entry.asset_name == LIBCORECLR_FILENAME) - { - m_coreclr_index = m_deps_entries.size() - 1; - trace::verbose(_X("Found coreclr from deps entry [%d] [%s, %s, %s]"), - m_coreclr_index, - entry.library_name.c_str(), - entry.library_version.c_str(), - entry.relative_path.c_str()); - } - } - return true; -} - -// ----------------------------------------------------------------------------- -// Parse the deps file. -// -// Returns: -// True if the file parse is successful or if file doesn't exist. False, -// when there is an error parsing the file. -// -bool deps_resolver_t::parse_deps_file(const arguments_t& args) -{ - m_deps_path = args.deps_path; - - return load(); -} - // ----------------------------------------------------------------------------- // Load local assemblies by priority order of their file extensions and // unique-fied by their simple name. // -void deps_resolver_t::get_local_assemblies(const pal::string_t& dir) +void deps_resolver_t::get_dir_assemblies( + const pal::string_t& dir, + const pal::string_t& dir_name, + std::unordered_map* dir_assemblies) { - trace::verbose(_X("Adding files from dir %s"), dir.c_str()); + trace::verbose(_X("Adding files from %s dir %s"), dir_name.c_str(), dir.c_str()); // Managed extensions in priority order, pick DLL over EXE and NI over IL. const pal::string_t managed_ext[] = { _X(".ni.dll"), _X(".dll"), _X(".ni.exe"), _X(".exe") }; @@ -412,18 +104,17 @@ void deps_resolver_t::get_local_assemblies(const pal::string_t& dir) continue; } - // TODO: Do a case insensitive lookup. // Already added entry for this asset, by priority order skip this ext - if (m_local_assemblies.count(file_name)) + if (dir_assemblies->count(file_name)) { - trace::verbose(_X("Skipping %s because the %s already exists in local assemblies"), file.c_str(), m_local_assemblies.find(file_name)->second.c_str()); + trace::verbose(_X("Skipping %s because the %s already exists in %s assemblies"), file.c_str(), dir_assemblies->find(file_name)->second.c_str(), dir_name.c_str()); continue; } // Add entry for this asset pal::string_t file_path = dir + DIR_SEPARATOR + file; - trace::verbose(_X("Adding %s to local assembly set from %s"), file_name.c_str(), file_path.c_str()); - m_local_assemblies.emplace(file_name, file_path); + trace::verbose(_X("Adding %s to %s assembly set from %s"), file_name.c_str(), dir_name.c_str(), file_path.c_str()); + dir_assemblies->emplace(file_name, file_path); } } } @@ -440,43 +131,93 @@ pal::string_t deps_resolver_t::resolve_coreclr_dir( const pal::string_t& package_dir, const pal::string_t& package_cache_dir) { - // Runtime servicing - trace::verbose(_X("Probing for CoreCLR in servicing dir=[%s]"), m_runtime_svc.c_str()); - if (!m_runtime_svc.empty()) + auto process_coreclr = [&] + (bool is_portable, const pal::string_t& deps_dir, deps_json_t* deps) -> pal::string_t { - pal::string_t svc_clr = m_runtime_svc; - append_path(&svc_clr, _X("runtime")); - append_path(&svc_clr, _X("coreclr")); + pal::string_t candidate; - if (coreclr_exists_in_dir(svc_clr)) + // Servicing override. + if (deps->has_coreclr_entry()) { - return svc_clr; + const deps_entry_t& entry = deps->get_coreclr_entry(); + trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in servicing"), entry.library_name.c_str(), entry.library_version.c_str()); + if (entry.is_serviceable && m_svc.find_redirection(entry.library_name, entry.library_version, entry.relative_path, &candidate)) + { + return get_directory(candidate); + } + } + else + { + trace::verbose(_X("Deps has no coreclr entry.")); } - } - // Package cache. - trace::verbose(_X("Probing for CoreCLR in package cache=[%s] deps index: [%d]"), package_cache_dir.c_str(), m_coreclr_index); - pal::string_t coreclr_cache; - if (m_coreclr_index >= 0 && !package_cache_dir.empty() && - m_deps_entries[m_coreclr_index].to_hash_matched_path(package_cache_dir, &coreclr_cache)) - { - return get_directory(coreclr_cache); - } + // Package cache. + pal::string_t coreclr_cache; + if (!package_cache_dir.empty()) + { + if (deps->has_coreclr_entry()) + { + const deps_entry_t& entry = deps->get_coreclr_entry(); + trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in package cache=[%s]"), entry.library_name.c_str(), entry.library_version.c_str(), package_cache_dir.c_str()); + if (entry.to_hash_matched_path(package_cache_dir, &coreclr_cache)) + { + return get_directory(coreclr_cache); + } + } + } - // App dir. - trace::verbose(_X("Probing for CoreCLR in app directory=[%s]"), app_dir.c_str()); - if (coreclr_exists_in_dir(app_dir)) - { - return app_dir; - } + // Deps directory: lookup relative path if portable, else look sxs. + if (is_portable) + { + pal::string_t coreclr_portable; + if (deps->has_coreclr_entry()) + { + const deps_entry_t& entry = deps->get_coreclr_entry(); + trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in portable app dir=[%s]"), entry.library_name.c_str(), entry.library_version.c_str(), deps_dir.c_str()); + if (entry.to_full_path(deps_dir, &coreclr_portable)) + { + return get_directory(coreclr_portable); + } + } + } + else + { + // App main dir or standalone app dir. + trace::verbose(_X("Probing for CoreCLR in deps directory=[%s]"), deps_dir.c_str()); + if (coreclr_exists_in_dir(deps_dir)) + { + return deps_dir; + } + } - // Packages dir - trace::verbose(_X("Probing for CoreCLR in packages=[%s] deps index: [%d]"), package_dir.c_str(), m_coreclr_index); - pal::string_t coreclr_package; - if (m_coreclr_index >= 0 && !package_dir.empty() && - m_deps_entries[m_coreclr_index].to_full_path(package_dir, &coreclr_package)) + // Packages dir. + pal::string_t coreclr_package; + if (!package_dir.empty()) + { + if (deps->has_coreclr_entry()) + { + const deps_entry_t& entry = deps->get_coreclr_entry(); + trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in packages dir=[%s]"), entry.library_name.c_str(), entry.library_version.c_str(), package_dir.c_str()); + if (entry.to_full_path(package_dir, &coreclr_package)) + { + return get_directory(coreclr_package); + } + } + } + + return pal::string_t(); + }; + + trace::info(_X("--- Starting CoreCLR Proble from app deps.json")); + pal::string_t clr_dir = process_coreclr(m_portable, app_dir, m_deps.get()); + if (clr_dir.empty() && m_portable) { - return get_directory(coreclr_package); + trace::info(_X("--- Starting CoreCLR Proble from FX deps.json")); + clr_dir = process_coreclr(false, m_fx_dir, m_fx_deps.get()); + } + if (!clr_dir.empty()) + { + return clr_dir; } // Use platform-specific search algorithm @@ -516,19 +257,33 @@ void deps_resolver_t::resolve_tpa_list( const pal::string_t& clr_dir, pal::string_t* output) { + std::vector empty(0); + + pal::string_t ni_package_cache_dir; + if (!package_cache_dir.empty()) + { + ni_package_cache_dir = package_cache_dir; + append_path(&ni_package_cache_dir, get_arch()); + } + // Obtain the local assemblies in the app dir. - get_local_assemblies(app_dir); + if (m_portable) + { + get_dir_assemblies(m_fx_dir, _X("fx"), &m_sxs_assemblies); + } + else + { + get_dir_assemblies(app_dir, _X("local"), &m_sxs_assemblies); + } std::set items; - add_mscorlib_to_tpa(clr_dir, &items, output); - - for (const deps_entry_t& entry : m_deps_entries) + auto process_entry = [&](bool is_portable, deps_json_t* deps, const deps_entry_t& entry) { // Is this asset a "runtime" type? - if (entry.asset_type != _X("runtime") || items.count(entry.asset_name)) + if (items.count(entry.asset_name)) { - continue; + return; } pal::string_t candidate; @@ -539,45 +294,63 @@ void deps_resolver_t::resolve_tpa_list( { add_tpa_asset(entry.asset_name, candidate, &items, output); } - // Is this entry present in the secondary package cache? + // Is an NI image for this entry present in the secondary package cache? + else if (entry.to_hash_matched_path(ni_package_cache_dir, &candidate)) + { + add_tpa_asset(entry.asset_name, candidate, &items, output); + } + // Is this entry present in the secondary package cache? (note: no .ni extension) else if (entry.to_hash_matched_path(package_cache_dir, &candidate)) { add_tpa_asset(entry.asset_name, candidate, &items, output); } // Is this entry present locally? - else if (m_local_assemblies.count(entry.asset_name)) + else if (!is_portable && m_sxs_assemblies.count(entry.asset_name)) { - // TODO: Case insensitive look up? - add_tpa_asset(entry.asset_name, m_local_assemblies.find(entry.asset_name)->second, &items, output); + add_tpa_asset(entry.asset_name, m_sxs_assemblies.find(entry.asset_name)->second, &items, output); } - // Is this entry present in the package restore dir? - else if (entry.to_full_path(package_dir, &candidate)) + // The app is portable so the asset should be picked up from relative subpath. + else if (is_portable && deps->try_ni(entry).to_full_path(app_dir, &candidate)) { add_tpa_asset(entry.asset_name, candidate, &items, output); } - } + // Is this entry present in the package restore dir? + else if (!package_dir.empty() && deps->try_ni(entry).to_full_path(package_dir, &candidate)) + { + add_tpa_asset(entry.asset_name, candidate, &items, output); + } + }; + + const auto& deps_entries = m_deps->get_entries(deps_entry_t::asset_types::runtime); + std::for_each(deps_entries.begin(), deps_entries.end(), [&](const deps_entry_t& entry) { + process_entry(m_portable, m_deps.get(), entry); + }); + const auto& fx_entries = m_portable ? m_fx_deps->get_entries(deps_entry_t::asset_types::runtime) : empty; + std::for_each(fx_entries.begin(), fx_entries.end(), [&](const deps_entry_t& entry) { + process_entry(false, m_fx_deps.get(), entry); + }); // Finally, if the deps file wasn't present or has missing entries, then // add the app local assemblies to the TPA. - for (const auto& kv : m_local_assemblies) + for (const auto& kv : m_sxs_assemblies) { add_tpa_asset(kv.first, kv.second, &items, output); } } // ----------------------------------------------------------------------------- -// Resolve the directories order for culture/native lookup +// Resolve the directories order for resources/native lookup // // Description: // This general purpose function specifies priority order of directory lookup -// for both native images and culture specific resource images. Lookup for -// culture assemblies is done by looking up two levels above from the file +// for both native images and resources specific resource images. Lookup for +// resources assemblies is done by looking up two levels above from the file // path. Lookup for native images is done by looking up one level from the // file path. // // Parameters: // asset_type - The type of the asset that needs lookup, currently -// supports "culture" and "native" +// supports "resources" and "native" // app_dir - The application local directory // package_dir - The directory path to where packages are restored // package_cache_dir - The directory path to secondary cache for packages @@ -594,60 +367,89 @@ void deps_resolver_t::resolve_probe_dirs( const pal::string_t& clr_dir, pal::string_t* output) { - assert(asset_type == _X("culture") || asset_type == _X("native")); + assert(asset_type == _X("resources") || asset_type == _X("native")); - // For culture assemblies, we need to provide the base directory of the culture path. + // For resources assemblies, we need to provide the base directory of the resources path. // For example: .../Foo/en-US/Bar.dll, then, the resolved path is .../Foo - std::function culture = [] (const pal::string_t& str) { + std::function resources = [] (const pal::string_t& str) { return get_directory(get_directory(str)); }; // For native assemblies, obtain the directory path from the file path std::function native = [] (const pal::string_t& str) { return get_directory(str); }; - std::function& action = (asset_type == _X("culture")) ? culture : native; - + std::function& action = (asset_type == _X("resources")) ? resources : native; + deps_entry_t::asset_types entry_type = (asset_type == _X("resources")) ? deps_entry_t::asset_types::resources : deps_entry_t::asset_types::native; std::set items; + std::vector empty(0); + const auto& entries = m_deps->get_entries(entry_type); + const auto& fx_entries = m_portable ? m_fx_deps->get_entries(entry_type) : empty; + // Fill the "output" with serviced DLL directories if they are serviceable // and have an entry present. - for (const deps_entry_t& entry : m_deps_entries) + auto add_serviced_entry = [&](const deps_entry_t& entry) { pal::string_t redirection_path; - if (entry.is_serviceable && entry.asset_type == asset_type && entry.library_type == _X("Package") && + if (entry.is_serviceable && entry.library_type == _X("Package") && m_svc.find_redirection(entry.library_name, entry.library_version, entry.relative_path, &redirection_path)) { add_unique_path(asset_type, action(redirection_path), &items, output); } - } + }; + + std::for_each(entries.begin(), entries.end(), add_serviced_entry); + std::for_each(fx_entries.begin(), fx_entries.end(), add_serviced_entry); pal::string_t candidate; // Take care of the secondary cache path if (!package_cache_dir.empty()) { - for (const deps_entry_t& entry : m_deps_entries) + auto add_package_cache_entry = [&](const deps_entry_t& entry) { - if (entry.asset_type == asset_type && entry.to_hash_matched_path(package_cache_dir, &candidate)) + if (entry.to_hash_matched_path(package_cache_dir, &candidate)) { add_unique_path(asset_type, action(candidate), &items, output); } - } + }; + std::for_each(entries.begin(), entries.end(), add_package_cache_entry); + std::for_each(fx_entries.begin(), fx_entries.end(), add_package_cache_entry); } // App local path add_unique_path(asset_type, app_dir, &items, output); - // Take care of the package restore path - if (!package_dir.empty()) + // For portable path, the app relative directory must be used. + if (m_portable) { - for (const deps_entry_t& entry : m_deps_entries) + std::for_each(entries.begin(), entries.end(), [&](const deps_entry_t& entry) { if (entry.asset_type == asset_type && entry.to_full_path(package_dir, &candidate)) { add_unique_path(asset_type, action(candidate), &items, output); } - } + }); + } + + // FX path if present + if (!m_fx_dir.empty()) + { + add_unique_path(asset_type, m_fx_dir, &items, output); + } + + // Take care of the package restore path + if (!package_dir.empty()) + { + auto add_packages_entry = [&](const deps_entry_t& entry) + { + if (entry.asset_type == asset_type && entry.to_full_path(package_dir, &candidate)) + { + add_unique_path(asset_type, action(candidate), &items, output); + } + }; + std::for_each(entries.begin(), entries.end(), add_packages_entry); + std::for_each(fx_entries.begin(), fx_entries.end(), add_packages_entry); } // CLR path @@ -656,7 +458,7 @@ void deps_resolver_t::resolve_probe_dirs( // ----------------------------------------------------------------------------- -// Entrypoint to resolve TPA, native and culture path ordering to pass to CoreCLR. +// Entrypoint to resolve TPA, native and resources path ordering to pass to CoreCLR. // // Parameters: // app_dir - The application local directory @@ -676,6 +478,6 @@ bool deps_resolver_t::resolve_probe_paths( { resolve_tpa_list(app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->tpa); resolve_probe_dirs(_X("native"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->native); - resolve_probe_dirs(_X("culture"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->culture); + resolve_probe_dirs(_X("resources"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->resources); return true; } diff --git a/src/corehost/cli/deps_resolver.h b/src/corehost/cli/deps_resolver.h index 309fce686..ce3d7c429 100644 --- a/src/corehost/cli/deps_resolver.h +++ b/src/corehost/cli/deps_resolver.h @@ -8,48 +8,45 @@ #include "pal.h" #include "trace.h" - +#include "deps_format.h" +#include "deps_entry.h" #include "servicing_index.h" - -struct deps_entry_t -{ - pal::string_t library_type; - pal::string_t library_name; - pal::string_t library_version; - pal::string_t library_hash; - pal::string_t asset_type; - pal::string_t asset_name; - pal::string_t relative_path; - bool is_serviceable; - - // Given a "base" dir, yield the relative path in the package layout. - bool to_full_path(const pal::string_t& root, pal::string_t* str) const; - - // Given a "base" dir, yield the relative path in the package layout only if - // the hash matches contents of the hash file. - bool to_hash_matched_path(const pal::string_t& root, pal::string_t* str) const; -}; +#include "runtime_config.h" // Probe paths to be resolved for ordering struct probe_paths_t { pal::string_t tpa; pal::string_t native; - pal::string_t culture; + pal::string_t resources; }; class deps_resolver_t { public: - deps_resolver_t(const arguments_t& args) + deps_resolver_t(const pal::string_t& fx_dir, const runtime_config_t* config, const arguments_t& args) : m_svc(args.dotnet_servicing) - , m_runtime_svc(args.dotnet_runtime_servicing) + , m_fx_dir(fx_dir) , m_coreclr_index(-1) + , m_portable(config->get_portable()) + , m_deps(nullptr) + , m_fx_deps(nullptr) { - m_deps_valid = parse_deps_file(args); + m_deps_file = args.deps_path; + if (m_portable) + { + m_fx_deps_file = get_fx_deps(fx_dir, config->get_fx_name()); + m_fx_deps = std::unique_ptr(new deps_json_t(false, m_fx_deps_file)); + m_deps = std::unique_ptr(new deps_json_t(true, m_deps_file, m_fx_deps->get_rid_fallback_graph())); + } + else + { + m_deps = std::unique_ptr(new deps_json_t(false, m_deps_file)); + } } - bool valid() { return m_deps_valid; } + + bool valid() { return m_deps->is_valid() && (!m_portable || m_fx_deps->is_valid()); } bool resolve_probe_paths( const pal::string_t& app_dir, @@ -63,11 +60,24 @@ public: const pal::string_t& package_dir, const pal::string_t& package_cache_dir); + const pal::string_t& get_fx_deps_file() const + { + return m_fx_deps_file; + } + + const pal::string_t& get_deps_file() const + { + return m_deps_file; + } private: - bool load(); - - bool parse_deps_file(const arguments_t& args); + static pal::string_t get_fx_deps(const pal::string_t& fx_dir, const pal::string_t& fx_name) + { + pal::string_t fx_deps = fx_dir; + pal::string_t fx_deps_name = pal::to_lower(fx_name) + _X(".deps.json"); + append_path(&fx_deps, fx_deps_name.c_str()); + return fx_deps; + } // Resolve order for TPA lookup. void resolve_tpa_list( @@ -86,30 +96,42 @@ private: const pal::string_t& clr_dir, pal::string_t* output); - // Populate local assemblies from app_dir listing. - void get_local_assemblies(const pal::string_t& dir); + // Populate assemblies from the directory. + void get_dir_assemblies( + const pal::string_t& dir, + const pal::string_t& dir_name, + std::unordered_map* dir_assemblies); // Servicing index to resolve serviced assembly paths. servicing_index_t m_svc; - // Runtime servicing directory. - pal::string_t m_runtime_svc; + // Framework deps file. + pal::string_t m_fx_dir; - // Map of simple name -> full path of local assemblies populated in priority - // order of their extensions. - std::unordered_map m_local_assemblies; - - // Entries in the dep file - std::vector m_deps_entries; + // Map of simple name -> full path of local/fx assemblies populated + // in priority order of their extensions. + std::unordered_map m_sxs_assemblies; // Special entry for coreclr in the deps entries int m_coreclr_index; - // The dep file path - pal::string_t m_deps_path; + // The filepath for the app deps + pal::string_t m_deps_file; + + // The filepath for the fx deps + pal::string_t m_fx_deps_file; + + // Deps files for the fx + std::unique_ptr m_fx_deps; + + // Deps files for the app + std::unique_ptr m_deps; // Is the deps file valid bool m_deps_valid; + + // Is the deps file portable app? + bool m_portable; }; #endif // DEPS_RESOLVER_H diff --git a/src/corehost/cli/dll/CMakeLists.txt b/src/corehost/cli/dll/CMakeLists.txt index 21248df7c..fd19ec602 100644 --- a/src/corehost/cli/dll/CMakeLists.txt +++ b/src/corehost/cli/dll/CMakeLists.txt @@ -13,16 +13,24 @@ endif() include(../setup.cmake) include_directories(../../common) +include_directories(../json/casablanca/include) # CMake does not recommend using globbing since it messes with the freshness checks set(SOURCES ../../common/trace.cpp ../../common/utils.cpp - + ../libhost.cpp + ../runtime_config.cpp + ../json/casablanca/src/json/json.cpp + ../json/casablanca/src/json/json_parsing.cpp + ../json/casablanca/src/json/json_serialization.cpp + ../json/casablanca/src/utilities/asyncrt_utils.cpp ../args.cpp ../hostpolicy.cpp ../coreclr.cpp ../deps_resolver.cpp + ../deps_format.cpp + ../deps_entry.cpp ../servicing_index.cpp) @@ -32,6 +40,8 @@ else() list(APPEND SOURCES ../../common/pal.unix.cpp) endif() +add_definitions(-D_NO_ASYNCRTIMP) +add_definitions(-D_NO_PPLXIMP) add_definitions(-DCOREHOST_MAKE_DLL=1) add_library(hostpolicy SHARED ${SOURCES}) diff --git a/src/corehost/cli/fxr/CMakeLists.txt b/src/corehost/cli/fxr/CMakeLists.txt new file mode 100644 index 000000000..43e0b54ca --- /dev/null +++ b/src/corehost/cli/fxr/CMakeLists.txt @@ -0,0 +1,45 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required (VERSION 2.6) +project(hostpolicy) + +if(WIN32) + add_compile_options($<$:/MT>) + add_compile_options($<$:/MT>) + add_compile_options($<$:/MTd>) +endif() + +include(../setup.cmake) + +include_directories(../../common) +include_directories(../json/casablanca/include) + +# CMake does not recommend using globbing since it messes with the freshness checks +set(SOURCES + ../../common/trace.cpp + ../../common/utils.cpp + ../../policy_load.cpp + ../libhost.cpp + ../runtime_config.cpp + ../json/casablanca/src/json/json.cpp + ../json/casablanca/src/json/json_parsing.cpp + ../json/casablanca/src/json/json_serialization.cpp + ../json/casablanca/src/utilities/asyncrt_utils.cpp + ./fx_ver.cpp + ./fx_muxer.cpp) + + +if(WIN32) + list(APPEND SOURCES ../../common/pal.windows.cpp) +else() + list(APPEND SOURCES ../../common/pal.unix.cpp) +endif() + +add_definitions(-D_NO_ASYNCRTIMP) +add_definitions(-D_NO_PPLXIMP) +add_definitions(-DCOREHOST_MAKE_DLL=1) + +add_library(hostfxr SHARED ${SOURCES}) + + diff --git a/src/corehost/cli/fxr/fx_muxer.cpp b/src/corehost/cli/fxr/fx_muxer.cpp new file mode 100644 index 000000000..84ae55c1b --- /dev/null +++ b/src/corehost/cli/fxr/fx_muxer.cpp @@ -0,0 +1,298 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include "pal.h" +#include "utils.h" +#include "libhost.h" +#include "args.h" +#include "fx_ver.h" +#include "fx_muxer.h" +#include "trace.h" +#include "runtime_config.h" +#include "cpprest/json.h" +#include "corehost.h" +#include "policy_load.h" + +typedef web::json::value json_value; + +pal::string_t fx_muxer_t::resolve_fx_dir(const pal::string_t& muxer_dir, runtime_config_t* runtime, const pal::string_t& app_path) +{ + const auto fx_name = runtime->get_fx_name(); + const auto fx_ver = runtime->get_fx_version(); + const auto roll_fwd = runtime->get_fx_roll_fwd(); + + fx_ver_t specified(-1, -1, -1); + if (!fx_ver_t::parse(fx_ver, &specified, false)) + { + return pal::string_t(); + } + + auto fx_dir = muxer_dir; + append_path(&fx_dir, _X("Shared")); + append_path(&fx_dir, fx_name.c_str()); + + // If not roll forward or if pre-release, just return. + if (!roll_fwd || specified.is_prerelease()) + { + append_path(&fx_dir, fx_ver.c_str()); + } + else + { + std::vector list; + pal::readdir(fx_dir, &list); + fx_ver_t max_specified = specified; + for (const auto& version : list) + { + fx_ver_t ver(-1, -1, -1); + if (fx_ver_t::parse(version, &ver, true) && + ver.get_major() == max_specified.get_major() && + ver.get_minor() == max_specified.get_minor()) + { + max_specified.set_patch(std::max(ver.get_patch(), max_specified.get_patch())); + } + } + pal::string_t max_specified_str = max_specified.as_str(); + append_path(&fx_dir, max_specified_str.c_str()); + } + trace::verbose(_X("Found fx in: %s"), fx_dir.c_str()); + return pal::directory_exists(fx_dir) ? fx_dir : pal::string_t(); +} + +pal::string_t fx_muxer_t::resolve_cli_version(const pal::string_t& global_json) +{ + pal::string_t retval; + if (!pal::file_exists(global_json)) + { + return retval; + } + + pal::ifstream_t file(global_json); + if (!file.good()) + { + return retval; + } + + try + { + const auto root = json_value::parse(file); + const auto& json = root.as_object(); + const auto sdk_iter = json.find(_X("sdk")); + if (sdk_iter == json.end() || sdk_iter->second.is_null()) + { + return retval; + } + + const auto& sdk_obj = sdk_iter->second.as_object(); + const auto ver_iter = sdk_obj.find(_X("version")); + if (ver_iter == sdk_obj.end() || ver_iter->second.is_null()) + { + return retval; + } + retval = ver_iter->second.as_string(); + } + catch (...) + { + } + trace::verbose(_X("Found cli in: %s"), retval.c_str()); + return retval; +} + +bool fx_muxer_t::resolve_sdk_dotnet_path(const pal::string_t& own_dir, pal::string_t* cli_sdk) +{ + pal::string_t cwd; + pal::string_t global; + if (pal::getcwd(&cwd)) + { + for (pal::string_t parent_dir, cur_dir = cwd; true; cur_dir = parent_dir) + { + pal::string_t file = cur_dir; + append_path(&file, _X("global.json")); + if (pal::file_exists(file)) + { + global = file; + break; + } + parent_dir = get_directory(cur_dir); + if (parent_dir.empty() || parent_dir.size() == cur_dir.size()) + { + break; + } + } + } + pal::string_t retval; + if (!global.empty()) + { + pal::string_t cli_version = resolve_cli_version(global); + if (!cli_version.empty()) + { + pal::string_t sdk_path = own_dir; + append_path(&sdk_path, _X("sdk")); + append_path(&sdk_path, cli_version.c_str()); + if (pal::directory_exists(sdk_path)) + { + retval = sdk_path; + } + } + } + if (retval.empty()) + { + pal::string_t sdk_path = own_dir; + append_path(&sdk_path, _X("sdk")); + + std::vector versions; + pal::readdir(sdk_path, &versions); + fx_ver_t max_ver(-1, -1, -1); + for (const auto& version : versions) + { + fx_ver_t ver(-1, -1, -1); + if (fx_ver_t::parse(version, &ver, true)) + { + max_ver = std::max(ver, max_ver); + } + } + pal::string_t max_ver_str = max_ver.as_str(); + append_path(&sdk_path, max_ver_str.c_str()); + if (pal::directory_exists(sdk_path)) + { + retval = sdk_path; + } + } + cli_sdk->assign(retval); + trace::verbose(_X("Found cli sdk in: %s"), cli_sdk->c_str()); + return !retval.empty(); +} + +/* static */ +int fx_muxer_t::execute(const int argc, const pal::char_t* argv[]) +{ + pal::string_t own_path; + + // Get the full name of the application + if (!pal::get_own_executable_path(&own_path) || !pal::realpath(&own_path)) + { + trace::error(_X("Failed to locate current executable")); + return StatusCode::LibHostCurExeFindFailure; + } + + auto own_dir = get_directory(own_path); + + if (argc <= 1) + { + return StatusCode::InvalidArgFailure; + } + if (ends_with(argv[1], _X(".dll"), false)) + { + pal::string_t app_path = argv[1]; + + if (!pal::realpath(&app_path)) + { + return StatusCode::LibHostExecModeFailure; + } + + runtime_config_t config(get_runtime_config_json(app_path)); + if (!config.is_valid()) + { + trace::error(_X("Invalid runtimeconfig.json [%s]"), config.get_path().c_str()); + return StatusCode::InvalidConfigFile; + } + if (config.get_portable()) + { + pal::string_t fx_dir = resolve_fx_dir(own_dir, &config, app_path); + corehost_init_t init(_X(""), _X(""), fx_dir, host_mode_t::muxer, &config); + return policy_load_t::execute_app(fx_dir, &init, argc, argv); + } + else + { + corehost_init_t init(_X(""), _X(""), _X(""), host_mode_t::muxer, &config); + return policy_load_t::execute_app(get_directory(app_path), &init, argc, argv); + } + } + else + { + if (pal::strcasecmp(_X("exec"), argv[1]) == 0) + { + std::vector known_opts = { _X("--depsfile"), _X("--additionalprobingpath") }; + + int num_args = 0; + std::unordered_map opts; + if (!parse_known_args(argc - 2, &argv[2], known_opts, &opts, &num_args)) + { + return InvalidArgFailure; + } + int cur_i = 2 + num_args; + if (cur_i >= argc) + { + return InvalidArgFailure; + } + + // Transform dotnet exec [--additionalprobingpath path] [--depsfile file] dll [args] -> dotnet dll [args] + + std::vector new_argv(argc - cur_i + 1); // +1 for dotnet + memcpy(new_argv.data() + 1, argv + cur_i, (argc - cur_i) * sizeof(pal::char_t*)); + new_argv[0] = argv[0]; + + pal::string_t deps_file = opts.count(_X("--depsfile")) ? opts[_X("--depsfile")] : _X(""); + pal::string_t probe_path = opts.count(_X("--additionalprobingpath")) ? opts[_X("--additionalprobingpath")] : _X(""); + + pal::string_t app_path = argv[cur_i]; + runtime_config_t config(get_runtime_config_json(app_path)); + if (!config.is_valid()) + { + trace::error(_X("Invalid runtimeconfig.json [%s]"), config.get_path().c_str()); + return StatusCode::InvalidConfigFile; + } + if (config.get_portable()) + { + pal::string_t fx_dir = resolve_fx_dir(own_dir, &config, app_path); + corehost_init_t init(deps_file, probe_path, fx_dir, host_mode_t::muxer, &config); + return policy_load_t::execute_app(fx_dir, &init, new_argv.size(), new_argv.data()); + } + else + { + corehost_init_t init(deps_file, probe_path, _X(""), host_mode_t::muxer, &config); + pal::string_t impl_dir = get_directory(deps_file.empty() ? app_path : deps_file); + return policy_load_t::execute_app(impl_dir, &init, new_argv.size(), new_argv.data()); + } + } + else + { + pal::string_t sdk_dotnet; + if (!resolve_sdk_dotnet_path(own_dir, &sdk_dotnet)) + { + return StatusCode::LibHostSdkFindFailure; + } + append_path(&sdk_dotnet, _X("dotnet.dll")); + // Transform dotnet [command] [args] -> dotnet [dotnet.dll] [command] [args] + + std::vector new_argv(argc + 1); + memcpy(&new_argv.data()[2], argv + 1, (argc - 1) * sizeof(pal::char_t*)); + new_argv[0] = argv[0]; + new_argv[1] = sdk_dotnet.c_str(); + + trace::verbose(_X("Using SDK dll=[%s]"), sdk_dotnet.c_str()); + + assert(ends_with(sdk_dotnet, _X(".dll"), false)); + + runtime_config_t config(get_runtime_config_json(sdk_dotnet)); + + if (config.get_portable()) + { + pal::string_t fx_dir = resolve_fx_dir(own_dir, &config, sdk_dotnet); + corehost_init_t init(_X(""), _X(""), fx_dir, host_mode_t::muxer, &config); + return policy_load_t::execute_app(fx_dir, &init, new_argv.size(), new_argv.data()); + } + else + { + corehost_init_t init(_X(""), _X(""), _X(""), host_mode_t::muxer, &config); + return policy_load_t::execute_app(get_directory(sdk_dotnet), &init, new_argv.size(), new_argv.data()); + } + } + } +} + +SHARED_API int hostfxr_main(const int argc, const pal::char_t* argv[]) +{ + trace::setup(); + return fx_muxer_t().execute(argc, argv); +} diff --git a/src/corehost/cli/fxr/fx_muxer.h b/src/corehost/cli/fxr/fx_muxer.h new file mode 100644 index 000000000..de6518a4c --- /dev/null +++ b/src/corehost/cli/fxr/fx_muxer.h @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +class runtime_config_t; +struct fx_ver_t; + +class fx_muxer_t +{ +public: + static int execute(const int argc, const pal::char_t* argv[]); +private: + static pal::string_t resolve_fx_dir(const pal::string_t& muxer_path, runtime_config_t* runtime, const pal::string_t& app_path); + static pal::string_t resolve_cli_version(const pal::string_t& global); + static bool resolve_sdk_dotnet_path(const pal::string_t& own_dir, pal::string_t* cli_sdk); +}; + diff --git a/src/corehost/cli/fxr/fx_ver.cpp b/src/corehost/cli/fxr/fx_ver.cpp new file mode 100644 index 000000000..7c81fb287 --- /dev/null +++ b/src/corehost/cli/fxr/fx_ver.cpp @@ -0,0 +1,168 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include "pal.h" +#include "fx_ver.h" + +fx_ver_t::fx_ver_t(int major, int minor, int patch, const pal::string_t& pre, const pal::string_t& build) + : m_major(major) + , m_minor(minor) + , m_patch(patch) + , m_pre(pre) + , m_build(build) +{ +} + +fx_ver_t::fx_ver_t(int major, int minor, int patch, const pal::string_t& pre) + : fx_ver_t(major, minor, patch, pre, _X("")) +{ +} + +fx_ver_t::fx_ver_t(int major, int minor, int patch) + : fx_ver_t(major, minor, patch, _X(""), _X("")) +{ +} + +bool fx_ver_t::operator ==(const fx_ver_t& b) const +{ + return compare(*this, b) == 0; +} + +bool fx_ver_t::operator !=(const fx_ver_t& b) const +{ + return !operator ==(b); +} + +bool fx_ver_t::operator <(const fx_ver_t& b) const +{ + return compare(*this, b) < 0; +} + +bool fx_ver_t::operator >(const fx_ver_t& b) const +{ + return compare(*this, b) > 0; +} + +pal::string_t fx_ver_t::as_str() +{ + pal::stringstream_t stream; + stream << m_major << _X(".") << m_minor << _X(".") << m_patch; + if (!m_pre.empty()) + { + stream << m_pre; + } + if (!m_build.empty()) + { + stream << _X("+") << m_build; + } + return stream.str(); +} + +/* static */ +int fx_ver_t::compare(const fx_ver_t&a, const fx_ver_t& b, bool ignore_build) +{ + // compare(u.v.w-p+b, x.y.z-q+c) + return + (a.m_major == b.m_major) + ? ((a.m_minor == b.m_minor) + ? ((a.m_patch == b.m_patch) + ? ((a.m_pre.empty() == b.m_pre.empty()) + ? ((a.m_pre.empty()) + ? (ignore_build ? 0 : a.m_build.compare(b.m_build)) + : a.m_pre.compare(b.m_pre)) + : a.m_pre.empty() ? 1 : -1) + : (a.m_patch > b.m_patch ? 1 : -1)) + : (a.m_minor > b.m_minor ? 1 : -1)) + : ((a.m_major > b.m_major) ? 1 : -1) + ; +} + +bool try_stou(const pal::string_t& str, unsigned* num) +{ + if (str.empty()) + { + return false; + } + if (str.find_first_not_of(_X("0123456789")) != pal::string_t::npos) + { + return false; + } + *num = (unsigned) std::stoul(str); + return true; +} + +bool parse_internal(const pal::string_t& ver, fx_ver_t* fx_ver, bool parse_only_production) +{ + size_t maj_start = 0; + size_t maj_sep = ver.find(_X('.')); + if (maj_sep == pal::string_t::npos) + { + return false; + } + unsigned major = 0; + if (!try_stou(ver.substr(maj_start, maj_sep), &major)) + { + return false; + } + + size_t min_start = maj_sep + 1; + size_t min_sep = ver.find(_X('.'), min_start); + if (min_sep == pal::string_t::npos) + { + return false; + } + + unsigned minor = 0; + if (!try_stou(ver.substr(min_start, min_sep - min_start), &minor)) + { + return false; + } + + unsigned patch = 0; + size_t pat_start = min_sep + 1; + size_t pat_sep = ver.find_first_not_of(_X("0123456789"), pat_start); + if (pat_sep == pal::string_t::npos) + { + if (!try_stou(ver.substr(pat_start), &patch)) + { + return false; + } + + *fx_ver = fx_ver_t(major, minor, patch); + return true; + } + + if (parse_only_production) + { + // This is a prerelease or has build suffix. + return false; + } + + if (!try_stou(ver.substr(pat_start, pat_sep - pat_start), &patch)) + { + return false; + } + + size_t pre_start = pat_sep; + size_t pre_sep = ver.find(_X('+'), pre_start); + if (pre_sep == pal::string_t::npos) + { + *fx_ver = fx_ver_t(major, minor, patch, ver.substr(pre_start)); + return true; + } + else + { + size_t build_start = pre_sep + 1; + *fx_ver = fx_ver_t(major, minor, patch, ver.substr(pre_start, pre_sep - pre_start), ver.substr(build_start)); + return true; + } +} + +/* static */ +bool fx_ver_t::parse(const pal::string_t& ver, fx_ver_t* fx_ver, bool parse_only_production) +{ + bool valid = parse_internal(ver, fx_ver, parse_only_production); + assert(!valid || fx_ver->as_str() == ver); + return valid; +} diff --git a/src/corehost/cli/fxr/fx_ver.h b/src/corehost/cli/fxr/fx_ver.h new file mode 100644 index 000000000..180426737 --- /dev/null +++ b/src/corehost/cli/fxr/fx_ver.h @@ -0,0 +1,40 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "pal.h" + +struct fx_ver_t +{ + fx_ver_t(int major, int minor, int patch); + fx_ver_t(int major, int minor, int patch, const pal::string_t& pre); + fx_ver_t(int major, int minor, int patch, const pal::string_t& pre, const pal::string_t& build); + + int get_major() { return m_major; } + int get_minor() { return m_minor; } + int get_patch() { return m_patch; } + + void set_major(int m) { m_major = m; } + void set_minor(int m) { m_minor = m; } + void set_patch(int p) { m_patch = p; } + + bool is_prerelease() { return !m_pre.empty(); } + + pal::string_t as_str(); + + bool operator ==(const fx_ver_t& b) const; + bool operator !=(const fx_ver_t& b) const; + bool operator <(const fx_ver_t& b) const; + bool operator >(const fx_ver_t& b) const; + + static bool parse(const pal::string_t& ver, fx_ver_t* fx_ver, bool parse_only_production = false); + +private: + int m_major; + int m_minor; + int m_patch; + pal::string_t m_pre; + pal::string_t m_build; + + static int compare(const fx_ver_t&a, const fx_ver_t& b, bool ignore_build = false); +}; + diff --git a/src/corehost/cli/hostpolicy.cpp b/src/corehost/cli/hostpolicy.cpp index b087357c8..32bdb924d 100644 --- a/src/corehost/cli/hostpolicy.cpp +++ b/src/corehost/cli/hostpolicy.cpp @@ -5,25 +5,21 @@ #include "args.h" #include "trace.h" #include "deps_resolver.h" +#include "fx_muxer.h" #include "utils.h" #include "coreclr.h" +#include "cpprest/json.h" +#include "libhost.h" +#include "error_codes.h" -enum StatusCode -{ - // 0x80 prefix to distinguish from corehost main's error codes. - InvalidArgFailure = 0x81, - CoreClrResolveFailure = 0x82, - CoreClrBindFailure = 0x83, - CoreClrInitFailure = 0x84, - CoreClrExeFailure = 0x85, - ResolverInitFailure = 0x86, - ResolverResolveFailure = 0x87, -}; -int run(const arguments_t& args) +corehost_init_t* g_init = nullptr; + +int run(const corehost_init_t* init, const runtime_config_t& config, const arguments_t& args) { // Load the deps resolver - deps_resolver_t resolver(args); + deps_resolver_t resolver(init->fx_dir(), &config, args); + if (!resolver.valid()) { trace::error(_X("Invalid .deps file")); @@ -31,8 +27,8 @@ int run(const arguments_t& args) } // Add packages directory - pal::string_t packages_dir = args.nuget_packages; - if (!pal::directory_exists(packages_dir)) + pal::string_t packages_dir = init->probe_dir(); + if (packages_dir.empty() || !pal::directory_exists(packages_dir)) { (void)pal::get_default_packages_directory(&packages_dir); } @@ -55,6 +51,8 @@ int run(const arguments_t& args) return StatusCode::ResolverResolveFailure; } + // TODO: config.get_runtime_properties(); + // Build CoreCLR properties const char* property_keys[] = { "TRUSTED_PLATFORM_ASSEMBLIES", @@ -63,20 +61,22 @@ int run(const arguments_t& args) "NATIVE_DLL_SEARCH_DIRECTORIES", "PLATFORM_RESOURCE_ROOTS", "AppDomainCompatSwitch", - // TODO: pipe this from corehost.json "SERVER_GC", // Workaround: mscorlib does not resolve symlinks for AppContext.BaseDirectory dotnet/coreclr/issues/2128 "APP_CONTEXT_BASE_DIRECTORY", + "APP_CONTEXT_DEPS_FILES" }; auto tpa_paths_cstr = pal::to_stdstring(probe_paths.tpa); auto app_base_cstr = pal::to_stdstring(args.app_dir); auto native_dirs_cstr = pal::to_stdstring(probe_paths.native); - auto culture_dirs_cstr = pal::to_stdstring(probe_paths.culture); + auto resources_dirs_cstr = pal::to_stdstring(probe_paths.resources); // Workaround for dotnet/cli Issue #488 and #652 pal::string_t server_gc; std::string server_gc_cstr = (pal::getenv(_X("COREHOST_SERVER_GC"), &server_gc) && !server_gc.empty()) ? pal::to_stdstring(server_gc) : "0"; + + std::string deps = pal::to_stdstring(resolver.get_deps_file() + _X(";") + resolver.get_fx_deps_file()); const char* property_values[] = { // TRUSTED_PLATFORM_ASSEMBLIES @@ -88,13 +88,15 @@ int run(const arguments_t& args) // NATIVE_DLL_SEARCH_DIRECTORIES native_dirs_cstr.c_str(), // PLATFORM_RESOURCE_ROOTS - culture_dirs_cstr.c_str(), + resources_dirs_cstr.c_str(), // AppDomainCompatSwitch "UseLatestBehaviorWhenTFMNotSpecified", // SERVER_GC server_gc_cstr.c_str(), // APP_CONTEXT_BASE_DIRECTORY - app_base_cstr.c_str() + app_base_cstr.c_str(), + // APP_CONTEXT_DEPS_FILES, + deps.c_str(), }; size_t property_size = sizeof(property_keys) / sizeof(property_keys[0]); @@ -188,16 +190,43 @@ int run(const arguments_t& args) return exit_code; } +SHARED_API int corehost_load(corehost_init_t* init) +{ + g_init = init; + return 0; +} + SHARED_API int corehost_main(const int argc, const pal::char_t* argv[]) { trace::setup(); + assert(g_init); + // Take care of arguments arguments_t args; - if (!parse_arguments(argc, argv, args)) + if (!parse_arguments(g_init->deps_file(), g_init->probe_dir(), g_init->host_mode(), argc, argv, &args)) { - return StatusCode::InvalidArgFailure; + return StatusCode::LibHostInvalidArgs; } - return run(args); + if (g_init->runtime_config()) + { + return run(g_init, *g_init->runtime_config(), args); + } + else + { + runtime_config_t config(get_runtime_config_json(args.managed_application)); + if (!config.is_valid()) + { + trace::error(_X("Invalid runtimeconfig.json [%s]"), config.get_path().c_str()); + return StatusCode::InvalidConfigFile; + } + return run(g_init, config, args); + } +} + +SHARED_API int corehost_unload() +{ + g_init = nullptr; + return 0; } diff --git a/src/corehost/cli/json/casablanca/LICENSE.txt b/src/corehost/cli/json/casablanca/LICENSE.txt new file mode 100644 index 000000000..314f8097f --- /dev/null +++ b/src/corehost/cli/json/casablanca/LICENSE.txt @@ -0,0 +1 @@ +https://github.com/Microsoft/cpprestsdk diff --git a/src/corehost/cli/json/casablanca/include/cpprest/asyncrt_utils.h b/src/corehost/cli/json/casablanca/include/cpprest/asyncrt_utils.h new file mode 100644 index 000000000..c100ece52 --- /dev/null +++ b/src/corehost/cli/json/casablanca/include/cpprest/asyncrt_utils.h @@ -0,0 +1,600 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Various common utilities. +* +* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "cpprest/details/basic_types.h" + +#if !defined(_WIN32) || (_MSC_VER >= 1700) +#include +#endif + +#ifndef _WIN32 +//#include +#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269 +#include +#endif +#endif + +/// Various utilities for string conversions and date and time manipulation. +namespace utility +{ + +// Left over from VS2010 support, remains to avoid breaking. +typedef std::chrono::seconds seconds; + +/// Functions for converting to/from std::chrono::seconds to xml string. +namespace timespan +{ + /// + /// Converts a timespan/interval in seconds to xml duration string as specified by + /// http://www.w3.org/TR/xmlschema-2/#duration + /// + _ASYNCRTIMP utility::string_t __cdecl seconds_to_xml_duration(utility::seconds numSecs); + + /// + /// Converts an xml duration to timespan/interval in seconds + /// http://www.w3.org/TR/xmlschema-2/#duration + /// + _ASYNCRTIMP utility::seconds __cdecl xml_duration_to_seconds(const utility::string_t ×panString); +} + +/// Functions for Unicode string conversions. +namespace conversions +{ + /// + /// Converts a UTF-16 string to a UTF-8 string. + /// + /// A two byte character UTF-16 string. + /// A single byte character UTF-8 string. + _ASYNCRTIMP std::string __cdecl utf16_to_utf8(const utf16string &w); + + /// + /// Converts a UTF-8 string to a UTF-16 + /// + /// A single byte character UTF-8 string. + /// A two byte character UTF-16 string. + _ASYNCRTIMP utf16string __cdecl utf8_to_utf16(const std::string &s); + + /// + /// Converts a ASCII (us-ascii) string to a UTF-16 string. + /// + /// A single byte character us-ascii string. + /// A two byte character UTF-16 string. + _ASYNCRTIMP utf16string __cdecl usascii_to_utf16(const std::string &s); + + /// + /// Converts a Latin1 (iso-8859-1) string to a UTF-16 string. + /// + /// A single byte character UTF-8 string. + /// A two byte character UTF-16 string. + _ASYNCRTIMP utf16string __cdecl latin1_to_utf16(const std::string &s); + + /// + /// Converts a Latin1 (iso-8859-1) string to a UTF-8 string. + /// + /// A single byte character UTF-8 string. + /// A single byte character UTF-8 string. + _ASYNCRTIMP utf8string __cdecl latin1_to_utf8(const std::string &s); + + /// + /// Converts to a platform dependent Unicode string type. + /// + /// A single byte character UTF-8 string. + /// A platform dependent string type. + _ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string &&s); + + /// + /// Converts to a platform dependent Unicode string type. + /// + /// A two byte character UTF-16 string. + /// A platform dependent string type. + _ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string &&s); + + /// + /// Converts to a platform dependent Unicode string type. + /// + /// A single byte character UTF-8 string. + /// A platform dependent string type. + _ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string &s); + + /// + /// Converts to a platform dependent Unicode string type. + /// + /// A two byte character UTF-16 string. + /// A platform dependent string type. + _ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string &s); + + /// + /// Converts to a UTF-16 from string. + /// + /// A single byte character UTF-8 string. + /// A two byte character UTF-16 string. + _ASYNCRTIMP utf16string __cdecl to_utf16string(const std::string &value); + + /// + /// Converts to a UTF-16 from string. + /// + /// A two byte character UTF-16 string. + /// A two byte character UTF-16 string. + _ASYNCRTIMP utf16string __cdecl to_utf16string(utf16string value); + + /// + /// Converts to a UTF-8 string. + /// + /// A single byte character UTF-8 string. + /// A single byte character UTF-8 string. + _ASYNCRTIMP std::string __cdecl to_utf8string(std::string value); + + /// + /// Converts to a UTF-8 string. + /// + /// A two byte character UTF-16 string. + /// A single byte character UTF-8 string. + _ASYNCRTIMP std::string __cdecl to_utf8string(const utf16string &value); + + /// + /// Encode the given byte array into a base64 string + /// + _ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector& data); + + /// + /// Encode the given 8-byte integer into a base64 string + /// + _ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data); + + /// + /// Decode the given base64 string to a byte array + /// + _ASYNCRTIMP std::vector __cdecl from_base64(const utility::string_t& str); + + template + utility::string_t print_string(const Source &val, const std::locale &loc) + { + utility::ostringstream_t oss; + oss.imbue(loc); + oss << val; + if (oss.bad()) + { + throw std::bad_cast(); + } + return oss.str(); + } + + template + utility::string_t print_string(const Source &val) + { + return print_string(val, std::locale()); + } + + template + Target scan_string(const utility::string_t &str, const std::locale &loc) + { + Target t; + utility::istringstream_t iss(str); + iss.imbue(loc); + iss >> t; + if (iss.bad()) + { + throw std::bad_cast(); + } + return t; + } + + template + Target scan_string(const utility::string_t &str) + { + return scan_string(str, std::locale()); + } +} + +namespace details +{ + /// + /// Cross platform RAII container for setting thread local locale. + /// + class scoped_c_thread_locale + { + public: + _ASYNCRTIMP scoped_c_thread_locale(); + _ASYNCRTIMP ~scoped_c_thread_locale(); + +#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269 +#ifdef _WIN32 + typedef _locale_t xplat_locale; +#else + typedef locale_t xplat_locale; +#endif + + static _ASYNCRTIMP xplat_locale __cdecl c_locale(); +#endif + private: +#ifdef _WIN32 + std::string m_prevLocale; + int m_prevThreadSetting; +#elif !(defined(ANDROID) || defined(__ANDROID__)) + locale_t m_prevLocale; +#endif + scoped_c_thread_locale(const scoped_c_thread_locale &); + scoped_c_thread_locale & operator=(const scoped_c_thread_locale &); + }; + + /// + /// Our own implementation of alpha numeric instead of std::isalnum to avoid + /// taking global lock for performance reasons. + /// + inline bool __cdecl is_alnum(char ch) + { + return (ch >= '0' && ch <= '9') + || (ch >= 'A' && ch <= 'Z') + || (ch >= 'a' && ch <= 'z'); + } + + /// + /// Simplistic implementation of make_unique. A better implementation would be based on variadic templates + /// and therefore not be compatible with Dev10. + /// + template + std::unique_ptr<_Type> make_unique() { + return std::unique_ptr<_Type>(new _Type()); + } + + template + std::unique_ptr<_Type> make_unique(_Arg1&& arg1) { + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1))); + } + + template + std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2) { + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2))); + } + + template + std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3) { + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3))); + } + + template + std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4) { + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4))); + } + + /// + /// Cross platform utility function for performing case insensitive string comparision. + /// + /// First string to compare. + /// Second strong to compare. + /// true if the strings are equivalent, false otherwise +/* inline bool str_icmp(const utility::string_t &left, const utility::string_t &right) + { +#ifdef _WIN32 + return _wcsicmp(left.c_str(), right.c_str()) == 0; +#else + return boost::iequals(left, right); +#endif + } +*/ +#ifdef _WIN32 + +/// +/// Category error type for Windows OS errors. +/// +class windows_category_impl : public std::error_category +{ +public: + virtual const char *name() const CPPREST_NOEXCEPT { return "windows"; } + + _ASYNCRTIMP virtual std::string message(int errorCode) const CPPREST_NOEXCEPT; + + _ASYNCRTIMP virtual std::error_condition default_error_condition(int errorCode) const CPPREST_NOEXCEPT; +}; + +/// +/// Gets the one global instance of the windows error category. +/// +/// An error category instance. +_ASYNCRTIMP const std::error_category & __cdecl windows_category(); + +#else + +/// +/// Gets the one global instance of the linux error category. +/// +/// An error category instance. +_ASYNCRTIMP const std::error_category & __cdecl linux_category(); + +#endif + +/// +/// Gets the one global instance of the current platform's error category. +/// +_ASYNCRTIMP const std::error_category & __cdecl platform_category(); + +/// +/// Creates an instance of std::system_error from a OS error code. +/// +inline std::system_error __cdecl create_system_error(unsigned long errorCode) +{ + std::error_code code((int)errorCode, platform_category()); + return std::system_error(code, code.message()); +} + +/// +/// Creates a std::error_code from a OS error code. +/// +inline std::error_code __cdecl create_error_code(unsigned long errorCode) +{ + return std::error_code((int)errorCode, platform_category()); +} + +/// +/// Creates the corresponding error message from a OS error code. +/// +inline utility::string_t __cdecl create_error_message(unsigned long errorCode) +{ + return utility::conversions::to_string_t(create_error_code(errorCode).message()); +} + +} + +class datetime +{ +public: + typedef uint64_t interval_type; + + /// + /// Defines the supported date and time string formats. + /// + enum date_format { RFC_1123, ISO_8601 }; + + /// + /// Returns the current UTC time. + /// + // static _ASYNCRTIMP datetime __cdecl utc_now(); + + /// + /// An invalid UTC timestamp value. + /// + enum:interval_type { utc_timestamp_invalid = static_cast(-1) }; + + /// + /// Returns seconds since Unix/POSIX time epoch at 01-01-1970 00:00:00. + /// If time is before epoch, utc_timestamp_invalid is returned. + /// + /* + static interval_type utc_timestamp() + { + const auto seconds = utc_now().to_interval() / _secondTicks; + if (seconds >= 11644473600LL) + { + return seconds - 11644473600LL; + } + else + { + return utc_timestamp_invalid; + } + } + */ + + datetime() : m_interval(0) + { + } + + /// + /// Creates datetime from a string representing time in UTC in RFC 1123 format. + /// + /// Returns a datetime of zero if not successful. + // static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123); + + /// + /// Returns a string representation of the datetime. + /// + _ASYNCRTIMP utility::string_t to_string(date_format format = RFC_1123) const; + + /// + /// Returns the integral time value. + /// + interval_type to_interval() const + { + return m_interval; + } + + datetime operator- (interval_type value) const + { + return datetime(m_interval - value); + } + + datetime operator+ (interval_type value) const + { + return datetime(m_interval + value); + } + + bool operator== (datetime dt) const + { + return m_interval == dt.m_interval; + } + + bool operator!= (const datetime& dt) const + { + return !(*this == dt); + } + + static interval_type from_milliseconds(unsigned int milliseconds) + { + return milliseconds*_msTicks; + } + + static interval_type from_seconds(unsigned int seconds) + { + return seconds*_secondTicks; + } + + static interval_type from_minutes(unsigned int minutes) + { + return minutes*_minuteTicks; + } + + static interval_type from_hours(unsigned int hours) + { + return hours*_hourTicks; + } + + static interval_type from_days(unsigned int days) + { + return days*_dayTicks; + } + + bool is_initialized() const + { + return m_interval != 0; + } + +private: + + friend int operator- (datetime t1, datetime t2); + + static const interval_type _msTicks = static_cast(10000); + static const interval_type _secondTicks = 1000*_msTicks; + static const interval_type _minuteTicks = 60*_secondTicks; + static const interval_type _hourTicks = 60*60*_secondTicks; + static const interval_type _dayTicks = 24*60*60*_secondTicks; + + +#ifdef _WIN32 + // void* to avoid pulling in windows.h + static _ASYNCRTIMP bool __cdecl datetime::system_type_to_datetime(/*SYSTEMTIME*/ void* psysTime, uint64_t seconds, datetime * pdt); +#else + static datetime timeval_to_datetime(const timeval &time); +#endif + + // Private constructor. Use static methods to create an instance. + datetime(interval_type interval) : m_interval(interval) + { + } + + // Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns. + interval_type m_interval; +}; + +#ifndef _WIN32 + +// temporary workaround for the fact that +// utf16char is not fully supported in GCC +class cmp +{ +public: + + static int icmp(std::string left, std::string right) + { + size_t i; + for (i = 0; i < left.size(); ++i) + { + if (i == right.size()) return 1; + + auto l = cmp::tolower(left[i]); + auto r = cmp::tolower(right[i]); + if (l > r) return 1; + if (l < r) return -1; + } + if (i < right.size()) return -1; + return 0; + } + +private: + static char tolower(char c) + { + if (c >= 'A' && c <= 'Z') + return static_cast(c - 'A' + 'a'); + return c; + } +}; + +#endif + +inline int operator- (datetime t1, datetime t2) +{ + auto diff = (t1.m_interval - t2.m_interval); + + // Round it down to seconds + diff /= 10 * 1000 * 1000; + + return static_cast(diff); +} + +/* +/// +/// Nonce string generator class. +/// +class nonce_generator +{ +public: + + /// + /// Define default nonce length. + /// + enum { default_length = 32 }; + + /// + /// Nonce generator constructor. + /// + /// Length of the generated nonce string. + nonce_generator(int length=default_length) : + m_random(static_cast(utility::datetime::utc_timestamp())), + m_length(length) + {} + + /// + /// Generate a nonce string containing random alphanumeric characters (A-Za-z0-9). + /// Length of the generated string is set by length(). + /// + /// The generated nonce string. + _ASYNCRTIMP utility::string_t generate(); + + /// + /// Get length of generated nonce string. + /// + /// Nonce string length. + int length() const { return m_length; } + + /// + /// Set length of the generated nonce string. + /// + /// Lenght of nonce string. + void set_length(int length) { m_length = length; } + +private: + static const utility::string_t c_allowed_chars; + std::mt19937 m_random; + int m_length; +}; +*/ +} // namespace utility; diff --git a/src/corehost/cli/json/casablanca/include/cpprest/details/SafeInt3.hpp b/src/corehost/cli/json/casablanca/include/cpprest/details/SafeInt3.hpp new file mode 100755 index 000000000..798012bed --- /dev/null +++ b/src/corehost/cli/json/casablanca/include/cpprest/details/SafeInt3.hpp @@ -0,0 +1,7048 @@ +/*----------------------------------------------------------------------------------------------------------- +SafeInt.hpp +Version 3.0.18p + +This software is licensed under the Microsoft Public License (Ms-PL). +For more information about Microsoft open source licenses, refer to +http://www.microsoft.com/opensource/licenses.mspx + +This license governs use of the accompanying software. If you use the software, you accept this license. +If you do not accept the license, do not use the software. + +Definitions +The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here +as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to +the software. A "contributor" is any person that distributes its contribution under this license. +"Licensed patents" are a contributor's patent claims that read directly on its contribution. + +Grant of Rights +(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations +in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to +reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution +or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in +section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed +patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution +in the software or derivative works of the contribution in the software. + +Conditions and Limitations +(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, + or trademarks. +(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the + software, your patent license from such contributor to the software ends automatically. +(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and + attribution notices that are present in the software. +(D) If you distribute any portion of the software in source code form, you may do so only under this license + by including a complete copy of this license with your distribution. If you distribute any portion of the + software in compiled or object code form, you may only do so under a license that complies with this license. +(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, + guarantees, or conditions. You may have additional consumer rights under your local laws which this license + cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties + of merchantability, fitness for a particular purpose and non-infringement. + + +Copyright (c) Microsoft Corporation. All rights reserved. + +This header implements an integer handling class designed to catch +unsafe integer operations + +This header compiles properly at Wall on Visual Studio, -Wall on gcc, and -Weverything on clang. + +Please read the leading comments before using the class. +---------------------------------------------------------------*/ +#pragma once + +// It is a bit tricky to sort out what compiler we are actually using, +// do this once here, and avoid cluttering the code +#define VISUAL_STUDIO_COMPILER 0 +#define CLANG_COMPILER 1 +#define GCC_COMPILER 2 +#define UNKNOWN_COMPILER -1 + +// Clang will sometimes pretend to be Visual Studio +// and does pretend to be gcc. Check it first, as nothing else pretends to be clang +#if defined __clang__ +#define SAFEINT_COMPILER CLANG_COMPILER +#elif defined __GNUC__ +#define SAFEINT_COMPILER GCC_COMPILER +#elif defined _MSC_VER +#define SAFEINT_COMPILER VISUAL_STUDIO_COMPILER +#else +#define SAFEINT_COMPILER UNKNOWN_COMPILER +#endif + +// Enable compiling with /Wall under VC +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning( push ) +// Disable warnings coming from headers +#pragma warning( disable:4987 4820 4987 4820 ) + +#endif + +// Need this for ptrdiff_t on some compilers +#include +#include + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER && defined _M_AMD64 + #include + #define SAFEINT_USE_INTRINSICS 1 +#else + #define SAFEINT_USE_INTRINSICS 0 +#endif + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning( pop ) +#endif + +// Various things needed for GCC +#if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER + +#define NEEDS_INT_DEFINED + +#if !defined NULL +#define NULL 0 +#endif + +// GCC warning suppression +#if SAFEINT_COMPILER == GCC_COMPILER +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#endif + +#include + +// clang only +#if SAFEINT_COMPILER == CLANG_COMPILER + +#if __has_feature(cxx_nullptr) + #define NEEDS_NULLPTR_DEFINED 0 +#endif + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++11-long-long" +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wunused-local-typedef" +#endif + +#endif + +// If the user made a choice, respect it #if !defined +#if !defined NEEDS_NULLPTR_DEFINED + // Visual Studio 2010 and higher support this + #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER + #if (_MSC_VER < 1600) + #define NEEDS_NULLPTR_DEFINED 1 + #else + #define NEEDS_NULLPTR_DEFINED 0 + #endif + #else + // Let everything else trigger based on whether we use c++11 or above + #if __cplusplus >= 201103L + #define NEEDS_NULLPTR_DEFINED 0 + #else + #define NEEDS_NULLPTR_DEFINED 1 + #endif + #endif +#endif + +#if NEEDS_NULLPTR_DEFINED +#define nullptr NULL +#endif + +#ifndef C_ASSERT +#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] +#endif + +// Let's test some assumptions +// We're assuming two's complement negative numbers +C_ASSERT( -1 == static_cast(0xffffffff) ); + +/************* Compiler Options ***************************************************************************************************** + +SafeInt supports several compile-time options that can change the behavior of the class. + +Compiler options: +SAFEINT_WARN_64BIT_PORTABILITY - this re-enables various warnings that happen when /Wp64 is used. Enabling this option is not + recommended. +NEEDS_INT_DEFINED - if your compiler does not support __int8, __int16, __int32 and __int64, you can enable this. +SAFEINT_ASSERT_ON_EXCEPTION - it is often easier to stop on an assert and figure out a problem than to try and figure out + how you landed in the catch block. +SafeIntDefaultExceptionHandler - if you'd like to replace the exception handlers SafeInt provides, define your replacement and + define this. Note - two built in (Windows-specific) options exist: + - SAFEINT_FAILFAST - bypasses all exception handlers, exits the app with an exception + - SAFEINT_RAISE_EXCEPTION - throws Win32 exceptions, which can be caught +SAFEINT_DISALLOW_UNSIGNED_NEGATION - Invoking the unary negation operator creates warnings, but if you'd like it to completely fail + to compile, define this. +ANSI_CONVERSIONS - This changes the class to use default comparison behavior, which may be unsafe. Enabling this + option is not recommended. +SAFEINT_DISABLE_BINARY_ASSERT - binary AND, OR or XOR operations on mixed size types can produce unexpected results. If you do + this, the default is to assert. Set this if you prefer not to assert under these conditions. +SIZE_T_CAST_NEEDED - some compilers complain if there is not a cast to size_t, others complain if there is one. + This lets you not have your compiler complain. +SAFEINT_DISABLE_SHIFT_ASSERT - Set this option if you don't want to assert when shifting more bits than the type has. Enabling + this option is not recommended. + +************************************************************************************************************************************/ + +/* +* The SafeInt class is designed to have as low an overhead as possible +* while still ensuring that all integer operations are conducted safely. +* Nearly every operator has been overloaded, with a very few exceptions. +* +* A usability-safety trade-off has been made to help ensure safety. This +* requires that every operation return either a SafeInt or a bool. If we +* allowed an operator to return a base integer type T, then the following +* can happen: +* +* char i = SafeInt(32) * 2 + SafeInt(16) * 4; +* +* The * operators take precedence, get overloaded, return a char, and then +* you have: +* +* char i = (char)64 + (char)64; //overflow! +* +* This situation would mean that safety would depend on usage, which isn't +* acceptable. +* +* One key operator that is missing is an implicit cast to type T. The reason for +* this is that if there is an implicit cast operator, then we end up with +* an ambiguous compile-time precedence. Because of this amiguity, there +* are two methods that are provided: +* +* Casting operators for every native integer type +* Version 3 note - it now compiles correctly for size_t without warnings +* +* SafeInt::Ptr() - returns the address of the internal integer +* Note - the '&' (address of) operator has been overloaded and returns +* the address of the internal integer. +* +* The SafeInt class should be used in any circumstances where ensuring +* integrity of the calculations is more important than performance. See Performance +* Notes below for additional information. +* +* Many of the conditionals will optimize out or be inlined for a release +* build (especially with /Ox), but it does have significantly more overhead, +* especially for signed numbers. If you do not _require_ negative numbers, use +* unsigned integer types - certain types of problems cannot occur, and this class +* performs most efficiently. +* +* Here's an example of when the class should ideally be used - +* +* void* AllocateMemForStructs(int StructSize, int HowMany) +* { +* SafeInt s(StructSize); +* +* s *= HowMany; +* +* return malloc(s); +* +* } +* +* Here's when it should NOT be used: +* +* void foo() +* { +* int i; +* +* for(i = 0; i < 0xffff; i++) +* .... +* } +* +* Error handling - a SafeInt class will throw exceptions if something +* objectionable happens. The exceptions are SafeIntException classes, +* which contain an enum as a code. +* +* Typical usage might be: +* +* bool foo() +* { +* SafeInt s; //note that s == 0 unless set +* +* try{ +* s *= 23; +* .... +* } +* catch(SafeIntException err) +* { +* //handle errors here +* } +* } +* +* Update for 3.0 - the exception class is now a template parameter. +* You can replace the exception class with any exception class you like. This is accomplished by: +* 1) Create a class that has the following interface: +* + template <> class YourSafeIntExceptionHandler < YourException > + { + public: + static __declspec(noreturn) void __stdcall SafeIntOnOverflow() + { + throw YourException( YourSafeIntArithmeticOverflowError ); + } + + static __declspec(noreturn) void __stdcall SafeIntOnDivZero() + { + throw YourException( YourSafeIntDivideByZeroError ); + } + }; +* +* Note that you don't have to throw C++ exceptions, you can throw Win32 exceptions, or do +* anything you like, just don't return from the call back into the code. +* +* 2) Either explicitly declare SafeInts like so: +* SafeInt< int, YourSafeIntExceptionHandler > si; +* or +* #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler +* +* Performance: +* +* Due to the highly nested nature of this class, you can expect relatively poor +* performance in unoptimized code. In tests of optimized code vs. correct inline checks +* in native code, this class has been found to take approximately 8% more CPU time (this varies), +* most of which is due to exception handling. Solutions: +* +* 1) Compile optimized code - /Ox is best, /O2 also performs well. Interestingly, /O1 +* (optimize for size) does not work as well. +* 2) If that 8% hit is really a serious problem, walk through the code and inline the +* exact same checks as the class uses. +* 3) Some operations are more difficult than others - avoid using signed integers, and if +* possible keep them all the same size. 64-bit integers are also expensive. Mixing +* different integer sizes and types may prove expensive. Be aware that literals are +* actually ints. For best performance, cast literals to the type desired. +* +* +* Performance update +* The current version of SafeInt uses template specialization to force the compiler to invoke only the +* operator implementation needed for any given pair of types. This will dramatically improve the perf +* of debug builds. +* +* 3.0 update - not only have we maintained the specialization, there were some cases that were overly complex, +* and using some additional cases (e.g. signed __int64 and unsigned __int64) resulted in some simplification. +* Additionally, there was a lot of work done to better optimize the 64-bit multiplication. +* +* Binary Operators +* +* All of the binary operators have certain assumptions built into the class design. +* This is to ensure correctness. Notes on each class of operator follow: +* +* Arithmetic Operators (*,/,+,-,%) +* There are three possible variants: +* SafeInt< T, E > op SafeInt< T, E > +* SafeInt< T, E > op U +* U op SafeInt< T, E > +* +* The SafeInt< T, E > op SafeInt< U, E > variant is explicitly not supported, and if you try to do +* this the compiler with throw the following error: +* +* error C2593: 'operator *' is ambiguous +* +* This is because the arithmetic operators are required to return a SafeInt of some type. +* The compiler cannot know whether you'd prefer to get a type T or a type U returned. If +* you need to do this, you need to extract the value contained within one of the two using +* the casting operator. For example: +* +* SafeInt< T, E > t, result; +* SafeInt< U, E > u; +* +* result = t * (U)u; +* +* Comparison Operators +* Because each of these operators return type bool, mixing SafeInts of differing types is +* allowed. +* +* Shift Operators +* Shift operators always return the type on the left hand side of the operator. Mixed type +* operations are allowed because the return type is always known. +* +* Boolean Operators +* Like comparison operators, these overloads always return type bool, and mixed-type SafeInts +* are allowed. Additionally, specific overloads exist for type bool on both sides of the +* operator. +* +* Binary Operators +* Mixed-type operations are discouraged, however some provision has been made in order to +* enable things like: +* +* SafeInt c = 2; +* +* if(c & 0x02) +* ... +* +* The "0x02" is actually an int, and it needs to work. +* In the case of binary operations on integers smaller than 32-bit, or of mixed type, corner +* cases do exist where you could get unexpected results. In any case where SafeInt returns a different +* result than the underlying operator, it will call assert(). You should examine your code and cast things +* properly so that you are not programming with side effects. +* +* Documented issues: +* +* This header compiles correctly at /W4 using VC++ 8 (Version 14.00.50727.42) and later. +* As of this writing, I believe it will also work for VC 7.1, but not for VC 7.0 or below. +* If you need a version that will work with lower level compilers, try version 1.0.7. None +* of them work with Visual C++ 6, and gcc didn't work very well, either, though this hasn't +* been tried recently. +* +* It is strongly recommended that any code doing integer manipulation be compiled at /W4 +* - there are a number of warnings which pertain to integer manipulation enabled that are +* not enabled at /W3 (default for VC++) +* +* Perf note - postfix operators are slightly more costly than prefix operators. +* Unless you're actually assigning it to something, ++SafeInt is less expensive than SafeInt++ +* +* The comparison operator behavior in this class varies from the ANSI definition, which is +* arguably broken. As an example, consider the following: +* +* unsigned int l = 0xffffffff; +* char c = -1; +* +* if(c == l) +* printf("Why is -1 equal to 4 billion???\n"); +* +* The problem here is that c gets cast to an int, now has a value of 0xffffffff, and then gets +* cast again to an unsigned int, losing the true value. This behavior is despite the fact that +* an __int64 exists, and the following code will yield a different (and intuitively correct) +* answer: +* +* if((__int64)c == (__int64)l)) +* printf("Why is -1 equal to 4 billion???\n"); +* else +* printf("Why doesn't the compiler upcast to 64-bits when needed?\n"); +* +* Note that combinations with smaller integers won't display the problem - if you +* changed "unsigned int" above to "unsigned short", you'd get the right answer. +* +* If you prefer to retain the ANSI standard behavior insert +* #define ANSI_CONVERSIONS +* into your source. Behavior differences occur in the following cases: +* 8, 16, and 32-bit signed int, unsigned 32-bit int +* any signed int, unsigned 64-bit int +* Note - the signed int must be negative to show the problem +* +* +* Revision history: +* +* Oct 12, 2003 - Created +* Author - David LeBlanc - dleblanc@microsoft.com +* +* Oct 27, 2003 - fixed numerous items pointed out by michmarc and bdawson +* Dec 28, 2003 - 1.0 +* added support for mixed-type operations +* thanks to vikramh +* also fixed broken __int64 multiplication section +* added extended support for mixed-type operations where possible +* Jan 28, 2004 - 1.0.1 +* changed WCHAR to wchar_t +* fixed a construct in two mixed-type assignment overloads that was +* not compiling on some compilers +* Also changed name of private method to comply with standards on +* reserved names +* Thanks to Niels Dekker for the input +* Feb 12, 2004 - 1.0.2 +* Minor changes to remove dependency on Windows headers +* Consistently used __int16, __int32 and __int64 to ensure +* portability +* May 10, 2004 - 1.0.3 +* Corrected bug in one case of GreaterThan +* July 22, 2004 - 1.0.4 +* Tightened logic in addition check (saving 2 instructions) +* Pulled error handler out into function to enable user-defined replacement +* Made internal type of SafeIntException an enum (as per Niels' suggestion) +* Added casts for base integer types (as per Scott Meyers' suggestion) +* Updated usage information - see important new perf notes. +* Cleaned up several const issues (more thanks to Niels) +* +* Oct 1, 2004 - 1.0.5 +* Added support for SEH exceptions instead of C++ exceptions - Win32 only +* Made handlers for DIV0 and overflows individually overridable +* Commented out the destructor - major perf gains here +* Added cast operator for type long, since long != __int32 +* Corrected a couple of missing const modifiers +* Fixed broken >= and <= operators for type U op SafeInt< T, E > +* Nov 5, 2004 - 1.0.6 +* Implemented new logic in binary operators to resolve issues with +* implicit casts +* Fixed casting operator because char != signed char +* Defined __int32 as int instead of long +* Removed unsafe SafeInt::Value method +* Re-implemented casting operator as a result of removing Value method +* Dec 1, 2004 - 1.0.7 +* Implemented specialized operators for pointer arithmetic +* Created overloads for cases of U op= SafeInt. What you do with U +* after that may be dangerous. +* Fixed bug in corner case of MixedSizeModulus +* Fixed bug in MixedSizeMultiply and MixedSizeDivision with input of 0 +* Added throw() decorations +* +* Apr 12, 2005 - 2.0 +* Extensive revisions to leverage template specialization. +* April, 2007 Extensive revisions for version 3.0 +* Nov 22, 2009 Forked from MS internal code +* Changes needed to support gcc compiler - many thanks to Niels Dekker +* for determining not just the issues, but also suggesting fixes. +* Also updating some of the header internals to be the same as the upcoming Visual Studio version. +* +* Jan 16, 2010 64-bit gcc has long == __int64, which means that many of the existing 64-bit +* templates are over-specialized. This forces a redefinition of all the 64-bit +* multiplication routines to use pointers instead of references for return +* values. Also, let's use some intrinsics for x64 Microsoft compiler to +* reduce code size, and hopefully improve efficiency. +* +* June 21, 2014 Better support for clang, higher warning levels supported for all 3 primary supported + compilers (Visual Studio, clang, gcc). + Also started to converge the code base such that the public CodePlex version will + be a drop-in replacement for the Visual Studio version. + +* Note about code style - throughout this class, casts will be written using C-style (T), +* not C++ style static_cast< T >. This is because the class is nearly always dealing with integer +* types, and in this case static_cast and a C cast are equivalent. Given the large number of casts, +* the code is a little more readable this way. In the event a cast is needed where static_cast couldn't +* be substituted, we'll use the new templatized cast to make it explicit what the operation is doing. +* +************************************************************************************************************ +* Version 3.0 changes: +* +* 1) The exception type thrown is now replacable, and you can throw your own exception types. This should help +* those using well-developed exception classes. +* 2) The 64-bit multiplication code has had a lot of perf work done, and should be faster than 2.0. +* 3) There is now limited floating point support. You can initialize a SafeInt with a floating point type, +* and you can cast it out (or assign) to a float as well. +* 4) There is now an Align method. I noticed people use this a lot, and rarely check errors, so now you have one. +* +* Another major improvement is the addition of external functions - if you just want to check an operation, this can now happen: +* All of the following can be invoked without dealing with creating a class, or managing exceptions. This is especially handy +* for 64-bit porting, since SafeCast compiles away for a 32-bit cast from size_t to unsigned long, but checks it for 64-bit. +* +* inline bool SafeCast( const T From, U& To ) throw() +* inline bool SafeEquals( const T t, const U u ) throw() +* inline bool SafeNotEquals( const T t, const U u ) throw() +* inline bool SafeGreaterThan( const T t, const U u ) throw() +* inline bool SafeGreaterThanEquals( const T t, const U u ) throw() +* inline bool SafeLessThan( const T t, const U u ) throw() +* inline bool SafeLessThanEquals( const T t, const U u ) throw() +* inline bool SafeModulus( const T& t, const U& u, T& result ) throw() +* inline bool SafeMultiply( T t, U u, T& result ) throw() +* inline bool SafeDivide( T t, U u, T& result ) throw() +* inline bool SafeAdd( T t, U u, T& result ) throw() +* inline bool SafeSubtract( T t, U u, T& result ) throw() +* +*/ + +//use these if the compiler does not support _intXX +#ifdef NEEDS_INT_DEFINED +#define __int8 char +#define __int16 short +#define __int32 int +#define __int64 long long +#endif + +namespace msl +{ + +namespace safeint3 +{ + +// catch these to handle errors +// Currently implemented code values: +// ERROR_ARITHMETIC_OVERFLOW +// EXCEPTION_INT_DIVIDE_BY_ZERO +enum SafeIntError +{ + SafeIntNoError = 0, + SafeIntArithmeticOverflow, + SafeIntDivideByZero +}; + +} // safeint3 +} // msl + + +/* +* Error handler classes +* Using classes to deal with exceptions is going to allow the most +* flexibility, and we can mix different error handlers in the same project +* or even the same file. It isn't advisable to do this in the same function +* because a SafeInt< int, MyExceptionHandler > isn't the same thing as +* SafeInt< int, YourExceptionHander >. +* If for some reason you have to translate between the two, cast one of them back to its +* native type. +* +* To use your own exception class with SafeInt, first create your exception class, +* which may look something like the SafeIntException class below. The second step is to +* create a template specialization that implements SafeIntOnOverflow and SafeIntOnDivZero. +* For example: +* +* template <> class SafeIntExceptionHandler < YourExceptionClass > +* { +* static __declspec(noreturn) void __stdcall SafeIntOnOverflow() +* { +* throw YourExceptionClass( EXCEPTION_INT_OVERFLOW ); +* } +* +* static __declspec(noreturn) void __stdcall SafeIntOnDivZero() +* { +* throw YourExceptionClass( EXCEPTION_INT_DIVIDE_BY_ZERO ); +* } +* }; +* +* typedef SafeIntExceptionHandler < YourExceptionClass > YourSafeIntExceptionHandler +* You'd then declare your SafeInt objects like this: +* SafeInt< int, YourSafeIntExceptionHandler > +* +* Unfortunately, there is no such thing as partial template specialization in typedef +* statements, so you have three options if you find this cumbersome: +* +* 1) Create a holder class: +* +* template < typename T > +* class MySafeInt +* { +* public: +* SafeInt< T, MyExceptionClass> si; +* }; +* +* You'd then declare an instance like so: +* MySafeInt< int > i; +* +* You'd lose handy things like initialization - it would have to be initialized as: +* +* i.si = 0; +* +* 2) You could create a typedef for every int type you deal with: +* +* typedef SafeInt< int, MyExceptionClass > MySafeInt; +* typedef SafeInt< char, MyExceptionClass > MySafeChar; +* +* and so on. The second approach is probably more usable, and will just drop into code +* better, which is the original intent of the SafeInt class. +* +* 3) If you're going to consistently use a different class to handle your exceptions, +* you can override the default typedef like so: +* +* #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler +* +* Overall, this is probably the best approach. +* */ + +// On the Microsoft compiler, violating a throw() annotation is a silent error. +// Other compilers might turn these into exceptions, and some users may want to not have throw() enabled. +// In addition, some error handlers may not throw C++ exceptions, which makes everything no throw. +#if defined SAFEINT_REMOVE_NOTHROW +#define SAFEINT_NOTHROW +#else +#define SAFEINT_NOTHROW throw() +#endif + +namespace msl +{ + +namespace safeint3 +{ + +// If you would like to use your own custom assert +// Define SAFEINT_ASSERT +#if !defined SAFEINT_ASSERT +#include +#define SAFEINT_ASSERT(x) assert(x) +#endif + +#if defined SAFEINT_ASSERT_ON_EXCEPTION + inline void SafeIntExceptionAssert() SAFEINT_NOTHROW { SAFEINT_ASSERT(false); } +#else + inline void SafeIntExceptionAssert() SAFEINT_NOTHROW {} +#endif + +#if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER + #define SAFEINT_NORETURN __attribute__((noreturn)) + #define SAFEINT_STDCALL + #define SAFEINT_VISIBLE __attribute__ ((__visibility__("default"))) + #define SAFEINT_WEAK __attribute__ ((weak)) +#else + #define SAFEINT_NORETURN __declspec(noreturn) + #define SAFEINT_STDCALL __stdcall + #define SAFEINT_VISIBLE + #define SAFEINT_WEAK +#endif + +class SAFEINT_VISIBLE SafeIntException +{ +public: + SafeIntException() SAFEINT_NOTHROW { m_code = SafeIntNoError; } + SafeIntException( SafeIntError code ) SAFEINT_NOTHROW + { + m_code = code; + } + SafeIntError m_code; +}; + +namespace SafeIntInternal +{ + // Visual Studio version of SafeInt provides for two possible error + // handlers: + // SafeIntErrorPolicy_SafeIntException - C++ exception, default if not otherwise defined + // SafeIntErrorPolicy_InvalidParameter - Calls fail fast (Windows-specific), bypasses any exception handlers, + // exits the app with a crash + template < typename E > class SafeIntExceptionHandler; + + template <> class SafeIntExceptionHandler < SafeIntException > + { + public: + + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() + { + SafeIntExceptionAssert(); + throw SafeIntException( SafeIntArithmeticOverflow ); + } + + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() + { + SafeIntExceptionAssert(); + throw SafeIntException( SafeIntDivideByZero ); + } + }; + +#if !defined _CRT_SECURE_INVALID_PARAMETER + // Calling fail fast is somewhat more robust than calling abort, + // but abort is the closest we can manage without Visual Studio support + // Need the header for abort() + #include + #define _CRT_SECURE_INVALID_PARAMETER(msg) abort() +#endif + + class SafeInt_InvalidParameter + { + public: + static SAFEINT_NORETURN void SafeIntOnOverflow() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + _CRT_SECURE_INVALID_PARAMETER("SafeInt Arithmetic Overflow"); + } + + static SAFEINT_NORETURN void SafeIntOnDivZero() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + _CRT_SECURE_INVALID_PARAMETER("SafeInt Divide By Zero"); + } + }; + +#if defined _WINDOWS_ + + class SafeIntWin32ExceptionHandler + { + public: + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + RaiseException( static_cast(EXCEPTION_INT_OVERFLOW), EXCEPTION_NONCONTINUABLE, 0, 0); + } + + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + RaiseException( static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), EXCEPTION_NONCONTINUABLE, 0, 0); + } + }; + +#endif + +} // namespace SafeIntInternal + +// both of these have cross-platform support +typedef SafeIntInternal::SafeIntExceptionHandler < SafeIntException > CPlusPlusExceptionHandler; +typedef SafeIntInternal::SafeInt_InvalidParameter InvalidParameterExceptionHandler; + +// This exception handler is no longer recommended, but is left here in order not to break existing users +#if defined _WINDOWS_ +typedef SafeIntInternal::SafeIntWin32ExceptionHandler Win32ExceptionHandler; +#endif + +// For Visual Studio compatibility +#if defined VISUAL_STUDIO_SAFEINT_COMPAT + typedef CPlusPlusExceptionHandler SafeIntErrorPolicy_SafeIntException; + typedef InvalidParameterExceptionHandler SafeIntErrorPolicy_InvalidParameter; +#endif + +// If the user hasn't defined a default exception handler, +// define one now, depending on whether they would like Win32 or C++ exceptions + +// This library will use conditional noexcept soon, but not in this release +// Some users might mix exception handlers, which is not advised, but is supported +#if !defined SafeIntDefaultExceptionHandler + #if defined SAFEINT_RAISE_EXCEPTION + #if !defined _WINDOWS_ + #error Include windows.h in order to use Win32 exceptions + #endif + + #define SafeIntDefaultExceptionHandler Win32ExceptionHandler + #elif defined SAFEINT_FAILFAST + #define SafeIntDefaultExceptionHandler InvalidParameterExceptionHandler + #else + #define SafeIntDefaultExceptionHandler CPlusPlusExceptionHandler + #if !defined SAFEINT_EXCEPTION_HANDLER_CPP + #define SAFEINT_EXCEPTION_HANDLER_CPP 1 + #endif + #endif +#endif + +#if !defined SAFEINT_EXCEPTION_HANDLER_CPP +#define SAFEINT_EXCEPTION_HANDLER_CPP 0 +#endif + +// If an error handler is chosen other than C++ exceptions, such as Win32 exceptions, fail fast, +// or abort, then all methods become no throw. Some teams track throw() annotations closely, +// and the following option provides for this. +#if SAFEINT_EXCEPTION_HANDLER_CPP +#define SAFEINT_CPP_THROW +#else +#define SAFEINT_CPP_THROW SAFEINT_NOTHROW +#endif + +// Turns out we can fool the compiler into not seeing compile-time constants with +// a simple template specialization +template < int method > class CompileConst; +template <> class CompileConst { public: static bool Value() SAFEINT_NOTHROW { return true; } }; +template <> class CompileConst { public: static bool Value() SAFEINT_NOTHROW { return false; } }; + +// The following template magic is because we're now not allowed +// to cast a float to an enum. This means that if we happen to assign +// an enum to a SafeInt of some type, it won't compile, unless we prevent +// isFloat = ( (T)( (float)1.1 ) > (T)1 ) +// from compiling in the case of an enum, which is the point of the specialization +// that follows. + +// If we have support for std, then we can do this easily, and detect enums as well +template < typename T > class NumericType; + +#if defined _LIBCPP_TYPE_TRAITS || defined _TYPE_TRAITS_ +// Continue to special case bool +template <> class NumericType { public: enum{ isBool = true, isFloat = false, isInt = false }; }; +template < typename T > class NumericType +{ + public: + enum + { + isBool = false, // We specialized out a bool + isFloat = std::is_floating_point::value, + // If it is an enum, then consider it an int type + // This does allow someone to make a SafeInt from an enum type, which is not recommended, + // but it also allows someone to add an enum value to a SafeInt, which is handy. + isInt = std::is_integral::value || std::is_enum::value + }; +}; + +#else + +template <> class NumericType { public: enum{ isBool = true, isFloat = false, isInt = false }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +#if defined SAFEINT_USE_WCHAR_T || defined _NATIVE_WCHAR_T_DEFINED +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +#endif +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType<__int64> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = true, isInt = false }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = true, isInt = false }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = true, isInt = false }; }; +// Catch-all for anything not supported +template < typename T > class NumericType +{ +public: + // We have some unknown type, which could be an enum. For parity with the code that uses , + // We can try a static_cast - it if compiles, then it might be an enum, and should work. + // If it is something else that just happens to have a constructor that takes an int, and a casting operator, + // then it is possible something will go wrong, and for best results, cast it directly to an int before letting it + // interact with a SafeInt + + enum + { + isBool = false, + isFloat = false, + isInt = static_cast( static_cast(0) ) == 0 + }; +}; +#endif // type traits + +// Use this to avoid compile-time const truncation warnings +template < int fSigned, int bits > class SafeIntMinMax; + +template <> class SafeIntMinMax< true, 8 > { public: const static signed __int8 min = (-0x7f - 1); + const static signed __int8 max = 0x7f; }; +template <> class SafeIntMinMax< true, 16 > { public: const static __int16 min = ( -0x7fff - 1 ); + const static __int16 max = 0x7fff; }; +template <> class SafeIntMinMax< true, 32 > { public: const static __int32 min = ( -0x7fffffff -1 ); + const static __int32 max = 0x7fffffff; }; +template <> class SafeIntMinMax< true, 64 > { public: const static __int64 min = static_cast<__int64>(0x8000000000000000LL); + const static __int64 max = 0x7fffffffffffffffLL; }; + +template <> class SafeIntMinMax< false, 8 > { public: const static unsigned __int8 min = 0; + const static unsigned __int8 max = 0xff; }; +template <> class SafeIntMinMax< false, 16 > { public: const static unsigned __int16 min = 0; + const static unsigned __int16 max = 0xffff; }; +template <> class SafeIntMinMax< false, 32 > { public: const static unsigned __int32 min = 0; + const static unsigned __int32 max = 0xffffffff; }; +template <> class SafeIntMinMax< false, 64 > { public: const static unsigned __int64 min = 0; + const static unsigned __int64 max = 0xffffffffffffffffULL; }; + +template < typename T > class IntTraits +{ +public: + C_ASSERT( NumericType::isInt ); + enum + { + isSigned = ( (T)(-1) < 0 ), + is64Bit = ( sizeof(T) == 8 ), + is32Bit = ( sizeof(T) == 4 ), + is16Bit = ( sizeof(T) == 2 ), + is8Bit = ( sizeof(T) == 1 ), + isLT32Bit = ( sizeof(T) < 4 ), + isLT64Bit = ( sizeof(T) < 8 ), + isInt8 = ( sizeof(T) == 1 && isSigned ), + isUint8 = ( sizeof(T) == 1 && !isSigned ), + isInt16 = ( sizeof(T) == 2 && isSigned ), + isUint16 = ( sizeof(T) == 2 && !isSigned ), + isInt32 = ( sizeof(T) == 4 && isSigned ), + isUint32 = ( sizeof(T) == 4 && !isSigned ), + isInt64 = ( sizeof(T) == 8 && isSigned ), + isUint64 = ( sizeof(T) == 8 && !isSigned ), + bitCount = ( sizeof(T)*8 ), + isBool = ( (T)2 == (T)1 ) + }; + + // On version 13.10 enums cannot define __int64 values + // so we'll use const statics instead! + // These must be cast to deal with the possibility of a SafeInt being given an enum as an argument + const static T maxInt = static_cast(SafeIntMinMax< isSigned, bitCount >::max); + const static T minInt = static_cast(SafeIntMinMax< isSigned, bitCount >::min); +}; + +template < typename T > +const T IntTraits< T >::maxInt; +template < typename T > +const T IntTraits< T >::minInt; + +template < typename T, typename U > class SafeIntCompare +{ +public: + enum + { + isBothSigned = (IntTraits< T >::isSigned && IntTraits< U >::isSigned), + isBothUnsigned = (!IntTraits< T >::isSigned && !IntTraits< U >::isSigned), + isLikeSigned = ((bool)(IntTraits< T >::isSigned) == (bool)(IntTraits< U >::isSigned)), + isCastOK = ((isLikeSigned && sizeof(T) >= sizeof(U)) || + (IntTraits< T >::isSigned && sizeof(T) > sizeof(U))), + isBothLT32Bit = (IntTraits< T >::isLT32Bit && IntTraits< U >::isLT32Bit), + isBothLT64Bit = (IntTraits< T >::isLT64Bit && IntTraits< U >::isLT64Bit) + }; +}; + +//all of the arithmetic operators can be solved by the same code within +//each of these regions without resorting to compile-time constant conditionals +//most operators collapse the problem into less than the 22 zones, but this is used +//as the first cut +//using this also helps ensure that we handle all of the possible cases correctly + +template < typename T, typename U > class IntRegion +{ +public: + enum + { + //unsigned-unsigned zone + IntZone_UintLT32_UintLT32 = SafeIntCompare< T,U >::isBothUnsigned && SafeIntCompare< T,U >::isBothLT32Bit, + IntZone_Uint32_UintLT64 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::is32Bit && IntTraits< U >::isLT64Bit, + IntZone_UintLT32_Uint32 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::isLT32Bit && IntTraits< U >::is32Bit, + IntZone_Uint64_Uint = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::is64Bit, + IntZone_UintLT64_Uint64 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::isLT64Bit && IntTraits< U >::is64Bit, + //unsigned-signed + IntZone_UintLT32_IntLT32 = !IntTraits< T >::isSigned && IntTraits< U >::isSigned && SafeIntCompare< T,U >::isBothLT32Bit, + IntZone_Uint32_IntLT64 = IntTraits< T >::isUint32 && IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit, + IntZone_UintLT32_Int32 = !IntTraits< T >::isSigned && IntTraits< T >::isLT32Bit && IntTraits< U >::isInt32, + IntZone_Uint64_Int = IntTraits< T >::isUint64 && IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit, + IntZone_UintLT64_Int64 = !IntTraits< T >::isSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::isInt64, + IntZone_Uint64_Int64 = IntTraits< T >::isUint64 && IntTraits< U >::isInt64, + //signed-signed + IntZone_IntLT32_IntLT32 = SafeIntCompare< T,U >::isBothSigned && SafeIntCompare< T, U >::isBothLT32Bit, + IntZone_Int32_IntLT64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::is32Bit && IntTraits< U >::isLT64Bit, + IntZone_IntLT32_Int32 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isLT32Bit && IntTraits< U >::is32Bit, + IntZone_Int64_Int64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isInt64 && IntTraits< U >::isInt64, + IntZone_Int64_Int = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::is64Bit && IntTraits< U >::isLT64Bit, + IntZone_IntLT64_Int64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::is64Bit, + //signed-unsigned + IntZone_IntLT32_UintLT32 = IntTraits< T >::isSigned && !IntTraits< U >::isSigned && SafeIntCompare< T,U >::isBothLT32Bit, + IntZone_Int32_UintLT32 = IntTraits< T >::isInt32 && !IntTraits< U >::isSigned && IntTraits< U >::isLT32Bit, + IntZone_IntLT64_Uint32 = IntTraits< T >::isSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::isUint32, + IntZone_Int64_UintLT64 = IntTraits< T >::isInt64 && !IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit, + IntZone_Int_Uint64 = IntTraits< T >::isSigned && IntTraits< U >::isUint64 && IntTraits< T >::isLT64Bit, + IntZone_Int64_Uint64 = IntTraits< T >::isInt64 && IntTraits< U >::isUint64 + }; +}; + + +// In all of the following functions, we have two versions +// One for SafeInt, which throws C++ (or possibly SEH) exceptions +// The non-throwing versions are for use by the helper functions that return success and failure. +// Some of the non-throwing functions are not used, but are maintained for completeness. + +// There's no real alternative to duplicating logic, but keeping the two versions +// immediately next to one another will help reduce problems + + +// useful function to help with getting the magnitude of a negative number +enum AbsMethod +{ + AbsMethodInt, + AbsMethodInt64, + AbsMethodNoop +}; + +template < typename T > +class GetAbsMethod +{ +public: + enum + { + method = IntTraits< T >::isLT64Bit && IntTraits< T >::isSigned ? AbsMethodInt : + IntTraits< T >::isInt64 ? AbsMethodInt64 : AbsMethodNoop + }; +}; + +// let's go ahead and hard-code a dependency on the +// representation of negative numbers to keep compilers from getting overly +// happy with optimizing away things like -MIN_INT. +template < typename T, int > class AbsValueHelper; + +template < typename T > class AbsValueHelper < T, AbsMethodInt> +{ +public: + static unsigned __int32 Abs( T t ) SAFEINT_NOTHROW + { + SAFEINT_ASSERT( t < 0 ); + return ~(unsigned __int32)t + 1; + } +}; + +template < typename T > class AbsValueHelper < T, AbsMethodInt64 > +{ +public: + static unsigned __int64 Abs( T t ) SAFEINT_NOTHROW + { + SAFEINT_ASSERT( t < 0 ); + return ~(unsigned __int64)t + 1; + } +}; + +template < typename T > class AbsValueHelper < T, AbsMethodNoop > +{ +public: + static T Abs( T t ) SAFEINT_NOTHROW + { + // Why are you calling Abs on an unsigned number ??? + SAFEINT_ASSERT( false ); + return t; + } +}; + +template < typename T, bool > class NegationHelper; +// Previous versions had an assert that the type being negated was 32-bit or higher +// In retrospect, this seems like something to just document +// Negation will normally upcast to int +// For example -(unsigned short)0xffff == (int)0xffff0001 +// This class will retain the type, and will truncate, which may not be what +// you wanted +// If you want normal operator casting behavior, do this: +// SafeInt ss = 0xffff; +// then: +// -(SafeInt(ss)) +// will then emit a signed int with the correct value and bitfield + +template < typename T > class NegationHelper // Signed +{ +public: + template + static T NegativeThrow( T t ) SAFEINT_CPP_THROW + { + // corner case + if( t != IntTraits< T >::minInt ) + { + // cast prevents unneeded checks in the case of small ints + return -t; + } + E::SafeIntOnOverflow(); + } + + static bool Negative( T t, T& ret ) SAFEINT_NOTHROW + { + // corner case + if( t != IntTraits< T >::minInt ) + { + // cast prevents unneeded checks in the case of small ints + ret = -t; + return true; + } + return false; + } +}; + +// Helper classes to work keep compilers from +// optimizing away negation +template < typename T > class SignedNegation; + +template <> +class SignedNegation +{ +public: + static signed __int32 Value(unsigned __int64 in) SAFEINT_NOTHROW + { + return (signed __int32)(~(unsigned __int32)in + 1); + } + + static signed __int32 Value(unsigned __int32 in) SAFEINT_NOTHROW + { + return (signed __int32)(~in + 1); + } +}; + +template <> +class SignedNegation +{ +public: + static signed __int64 Value(unsigned __int64 in) SAFEINT_NOTHROW + { + return (signed __int64)(~in + 1); + } +}; + +template < typename T > class NegationHelper // unsigned +{ +public: + template + static T NegativeThrow( T t ) SAFEINT_CPP_THROW + { +#if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION + C_ASSERT( sizeof(T) == 0 ); +#endif + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning(push) +//this avoids warnings from the unary '-' operator being applied to unsigned numbers +#pragma warning(disable:4146) +#endif + // Note - this could be quenched on gcc + // by doing something like: + // return (T)-((__int64)t); + // but it seems like you would want a warning when doing this. + return (T)-t; + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning(pop) +#endif + } + + static bool Negative( T t, T& ret ) SAFEINT_NOTHROW + { + if( IntTraits::isLT32Bit ) + { + // See above + SAFEINT_ASSERT( false ); + } +#if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION + C_ASSERT( sizeof(T) == 0 ); +#endif + // Do it this way to avoid warning + ret = -t; + return true; + } +}; + +//core logic to determine casting behavior +enum CastMethod +{ + CastOK = 0, + CastCheckLTZero, + CastCheckGTMax, + CastCheckSafeIntMinMaxUnsigned, + CastCheckSafeIntMinMaxSigned, + CastToFloat, + CastFromFloat, + CastToBool, + CastFromBool +}; + + +template < typename ToType, typename FromType > +class GetCastMethod +{ +public: + enum + { + method = ( IntTraits< FromType >::isBool && + !IntTraits< ToType >::isBool ) ? CastFromBool : + + ( !IntTraits< FromType >::isBool && + IntTraits< ToType >::isBool ) ? CastToBool : + + ( SafeIntCompare< ToType, FromType >::isCastOK ) ? CastOK : + + ( ( IntTraits< ToType >::isSigned && + !IntTraits< FromType >::isSigned && + sizeof( FromType ) >= sizeof( ToType ) ) || + ( SafeIntCompare< ToType, FromType >::isBothUnsigned && + sizeof( FromType ) > sizeof( ToType ) ) ) ? CastCheckGTMax : + + ( !IntTraits< ToType >::isSigned && + IntTraits< FromType >::isSigned && + sizeof( ToType ) >= sizeof( FromType ) ) ? CastCheckLTZero : + + ( !IntTraits< ToType >::isSigned ) ? CastCheckSafeIntMinMaxUnsigned + : CastCheckSafeIntMinMaxSigned + }; +}; + +template < typename FromType > class GetCastMethod < float, FromType > +{ +public: + enum{ method = CastOK }; +}; + +template < typename FromType > class GetCastMethod < double, FromType > +{ +public: + enum{ method = CastOK }; +}; + +template < typename FromType > class GetCastMethod < long double, FromType > +{ +public: + enum{ method = CastOK }; +}; + +template < typename ToType > class GetCastMethod < ToType, float > +{ +public: + enum{ method = CastFromFloat }; +}; + +template < typename ToType > class GetCastMethod < ToType, double > +{ +public: + enum{ method = CastFromFloat }; +}; + +template < typename ToType > class GetCastMethod < ToType, long double > +{ +public: + enum{ method = CastFromFloat }; +}; + +template < typename T, typename U, int > class SafeCastHelper; + +template < typename T, typename U > class SafeCastHelper < T, U, CastOK > +{ +public: + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + t = (T)u; + return true; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + t = (T)u; + } +}; + +// special case floats and doubles +// tolerate loss of precision +template < typename T, typename U > class SafeCastHelper < T, U, CastFromFloat > +{ +public: + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + if( u <= (U)IntTraits< T >::maxInt && + u >= (U)IntTraits< T >::minInt ) + { + t = (T)u; + return true; + } + return false; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + if( u <= (U)IntTraits< T >::maxInt && + u >= (U)IntTraits< T >::minInt ) + { + t = (T)u; + return; + } + E::SafeIntOnOverflow(); + } +}; + +// Match on any method where a bool is cast to type T +template < typename T > class SafeCastHelper < T, bool, CastFromBool > +{ +public: + static bool Cast( bool b, T& t ) SAFEINT_NOTHROW + { + t = (T)( b ? 1 : 0 ); + return true; + } + + template < typename E > + static void CastThrow( bool b, T& t ) SAFEINT_CPP_THROW + { + t = (T)( b ? 1 : 0 ); + } +}; + +template < typename T > class SafeCastHelper < bool, T, CastToBool > +{ +public: + static bool Cast( T t, bool& b ) SAFEINT_NOTHROW + { + b = !!t; + return true; + } + + template < typename E > + static void CastThrow( bool b, T& t ) SAFEINT_CPP_THROW + { + b = !!t; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckLTZero > +{ +public: + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + if( u < 0 ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + if( u < 0 ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckGTMax > +{ +public: + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + if( u > (U)IntTraits< T >::maxInt ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + if( u > (U)IntTraits< T >::maxInt ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckSafeIntMinMaxUnsigned > +{ +public: + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + // U is signed - T could be either signed or unsigned + if( u > IntTraits< T >::maxInt || u < 0 ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + // U is signed - T could be either signed or unsigned + if( u > IntTraits< T >::maxInt || u < 0 ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckSafeIntMinMaxSigned > +{ +public: + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + // T, U are signed + if( u > IntTraits< T >::maxInt || u < IntTraits< T >::minInt ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + //T, U are signed + if( u > IntTraits< T >::maxInt || u < IntTraits< T >::minInt ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +//core logic to determine whether a comparison is valid, or needs special treatment +enum ComparisonMethod +{ + ComparisonMethod_Ok = 0, + ComparisonMethod_CastInt, + ComparisonMethod_CastInt64, + ComparisonMethod_UnsignedT, + ComparisonMethod_UnsignedU +}; + + // Note - the standard is arguably broken in the case of some integer + // conversion operations + // For example, signed char a = -1 = 0xff + // unsigned int b = 0xffffffff + // If you then test if a < b, a value-preserving cast + // is made, and you're essentially testing + // (unsigned int)a < b == false + // + // I do not think this makes sense - if you perform + // a cast to an __int64, which can clearly preserve both value and signedness + // then you get a different and intuitively correct answer + // IMHO, -1 should be less than 4 billion + // If you prefer to retain the ANSI standard behavior + // insert #define ANSI_CONVERSIONS into your source + // Behavior differences occur in the following cases: + // 8, 16, and 32-bit signed int, unsigned 32-bit int + // any signed int, unsigned 64-bit int + // Note - the signed int must be negative to show the problem + +template < typename T, typename U > +class ValidComparison +{ +public: + enum + { +#ifdef ANSI_CONVERSIONS + method = ComparisonMethod_Ok +#else + method = ( ( SafeIntCompare< T, U >::isLikeSigned ) ? ComparisonMethod_Ok : + ( ( IntTraits< T >::isSigned && sizeof(T) < 8 && sizeof(U) < 4 ) || + ( IntTraits< U >::isSigned && sizeof(T) < 4 && sizeof(U) < 8 ) ) ? ComparisonMethod_CastInt : + ( ( IntTraits< T >::isSigned && sizeof(U) < 8 ) || + ( IntTraits< U >::isSigned && sizeof(T) < 8 ) ) ? ComparisonMethod_CastInt64 : + ( !IntTraits< T >::isSigned ) ? ComparisonMethod_UnsignedT : + ComparisonMethod_UnsignedU ) +#endif + }; +}; + +template class EqualityTest; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_Ok > +{ +public: + static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( t == u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt > +{ +public: + static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( (int)t == (int)u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt64 > +{ +public: + static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( (__int64)t == (__int64)u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedT > +{ +public: + static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW + { + //one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( u < 0 ) + return false; + + //else safe to cast to type T + return ( t == (T)u ); + } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedU> +{ +public: + static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW + { + //one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( t < 0 ) + return false; + + //else safe to cast to type U + return ( (U)t == u ); + } +}; + +template class GreaterThanTest; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_Ok > +{ +public: + static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( t > u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt > +{ +public: + static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( (int)t > (int)u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt64 > +{ +public: + static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( (__int64)t > (__int64)u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedT > +{ +public: + static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW + { + // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( u < 0 ) + return true; + + // else safe to cast to type T + return ( t > (T)u ); + } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedU > +{ +public: + static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW + { + // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( t < 0 ) + return false; + + // else safe to cast to type U + return ( (U)t > u ); + } +}; + +// Modulus is simpler than comparison, but follows much the same logic +// using this set of functions, it can't fail except in a div 0 situation +template class ModulusHelper; + +template class ModulusHelper +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //trap corner case + if( CompileConst< IntTraits< U >::isSigned >::Value() ) + { + // Some compilers don't notice that this only compiles when u is signed + // Add cast to make them happy + if( u == (U)-1 ) + { + result = 0; + return SafeIntNoError; + } + } + + result = (T)(t % u); + return SafeIntNoError; + } + + template < typename E > + static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + //trap corner case + if( CompileConst< IntTraits< U >::isSigned >::Value() ) + { + if( u == (U)-1 ) + { + result = 0; + return; + } + } + + result = (T)(t % u); + } +}; + +template class ModulusHelper +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //trap corner case + if( CompileConst< IntTraits< U >::isSigned >::Value() ) + { + if( u == (U)-1 ) + { + result = 0; + return SafeIntNoError; + } + } + + result = (T)(t % u); + return SafeIntNoError; + } + + template < typename E > + static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + //trap corner case + if( CompileConst< IntTraits< U >::isSigned >::Value() ) + { + if( u == (U)-1 ) + { + result = 0; + return; + } + } + + result = (T)(t % u); + } +}; + +template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_CastInt64> +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //trap corner case + if( CompileConst< IntTraits< U >::isSigned >::Value() ) + { + if( u == (U)-1 ) + { + result = 0; + return SafeIntNoError; + } + } + + result = (T)((__int64)t % (__int64)u); + return SafeIntNoError; + } + + template < typename E > + static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + if( CompileConst< IntTraits< U >::isSigned >::Value() ) + { + if( u == (U)-1 ) + { + result = 0; + return; + } + } + + result = (T)((__int64)t % (__int64)u); + } +}; + +// T is unsigned __int64, U is any signed int +template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_UnsignedT> +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + // u could be negative - if so, need to convert to positive + // casts below are always safe due to the way modulus works + if(u < 0) + result = (T)(t % AbsValueHelper< U, GetAbsMethod< U >::method >::Abs(u)); + else + result = (T)(t % u); + + return SafeIntNoError; + } + + template < typename E > + static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + // u could be negative - if so, need to convert to positive + if(u < 0) + result = (T)(t % AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u )); + else + result = (T)(t % u); + } +}; + +// U is unsigned __int64, T any signed int +template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_UnsignedU> +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //t could be negative - if so, need to convert to positive + if(t < 0) + result = (T)( ~( AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( t ) % u ) + 1 ); + else + result = (T)((T)t % u); + + return SafeIntNoError; + } + + template < typename E > + static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + //t could be negative - if so, need to convert to positive + if(t < 0) + result = (T)( ~( AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( t ) % u ) + 1); + else + result = (T)( (T)t % u ); + } +}; + +//core logic to determine method to check multiplication +enum MultiplicationState +{ + MultiplicationState_CastInt = 0, // One or both signed, smaller than 32-bit + MultiplicationState_CastInt64, // One or both signed, smaller than 64-bit + MultiplicationState_CastUint, // Both are unsigned, smaller than 32-bit + MultiplicationState_CastUint64, // Both are unsigned, both 32-bit or smaller + MultiplicationState_Uint64Uint, // Both are unsigned, lhs 64-bit, rhs 32-bit or smaller + MultiplicationState_Uint64Uint64, // Both are unsigned int64 + MultiplicationState_Uint64Int, // lhs is unsigned int64, rhs int32 + MultiplicationState_Uint64Int64, // lhs is unsigned int64, rhs signed int64 + MultiplicationState_UintUint64, // Both are unsigned, lhs 32-bit or smaller, rhs 64-bit + MultiplicationState_UintInt64, // lhs unsigned 32-bit or less, rhs int64 + MultiplicationState_Int64Uint, // lhs int64, rhs unsigned int32 + MultiplicationState_Int64Int64, // lhs int64, rhs int64 + MultiplicationState_Int64Int, // lhs int64, rhs int32 + MultiplicationState_IntUint64, // lhs int, rhs unsigned int64 + MultiplicationState_IntInt64, // lhs int, rhs int64 + MultiplicationState_Int64Uint64, // lhs int64, rhs uint64 + MultiplicationState_Error +}; + +template < typename T, typename U > +class MultiplicationMethod +{ +public: + enum + { + // unsigned-unsigned + method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? MultiplicationState_CastUint : + (IntRegion< T,U >::IntZone_Uint32_UintLT64 || + IntRegion< T,U >::IntZone_UintLT32_Uint32) ? MultiplicationState_CastUint64 : + SafeIntCompare< T,U >::isBothUnsigned && + IntTraits< T >::isUint64 && IntTraits< U >::isUint64 ? MultiplicationState_Uint64Uint64 : + (IntRegion< T,U >::IntZone_Uint64_Uint) ? MultiplicationState_Uint64Uint : + (IntRegion< T,U >::IntZone_UintLT64_Uint64) ? MultiplicationState_UintUint64 : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Uint64_Int) ? MultiplicationState_Uint64Int : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? MultiplicationState_UintInt64 : + (IntRegion< T,U >::IntZone_Uint64_Int64) ? MultiplicationState_Uint64Int64 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Int64_Int64) ? MultiplicationState_Int64Int64 : + (IntRegion< T,U >::IntZone_Int64_Int) ? MultiplicationState_Int64Int : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? MultiplicationState_IntInt64 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? MultiplicationState_Int64Uint : + (IntRegion< T,U >::IntZone_Int_Uint64) ? MultiplicationState_IntUint64 : + (IntRegion< T,U >::IntZone_Int64_Uint64 ? MultiplicationState_Int64Uint64 : + MultiplicationState_Error ) ) + }; +}; + +template class MultiplicationHelper; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastInt> +{ +public: + //accepts signed, both less than 32-bit + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + int tmp = t * u; + + if( tmp > IntTraits< T >::maxInt || tmp < IntTraits< T >::minInt ) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + int tmp = t * u; + + if( tmp > IntTraits< T >::maxInt || tmp < IntTraits< T >::minInt ) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastUint > +{ +public: + //accepts unsigned, both less than 32-bit + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + unsigned int tmp = (unsigned int)(t * u); + + if( tmp > IntTraits< T >::maxInt ) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + unsigned int tmp = (unsigned int)( t * u ); + + if( tmp > IntTraits< T >::maxInt ) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastInt64> +{ +public: + //mixed signed or both signed where at least one argument is 32-bit, and both a 32-bit or less + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + __int64 tmp = (__int64)t * (__int64)u; + + if(tmp > (__int64)IntTraits< T >::maxInt || tmp < (__int64)IntTraits< T >::minInt) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + __int64 tmp = (__int64)t * (__int64)u; + + if(tmp > (__int64)IntTraits< T >::maxInt || tmp < (__int64)IntTraits< T >::minInt) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastUint64> +{ +public: + //both unsigned where at least one argument is 32-bit, and both are 32-bit or less + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + unsigned __int64 tmp = (unsigned __int64)t * (unsigned __int64)u; + + if(tmp > (unsigned __int64)IntTraits< T >::maxInt) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + unsigned __int64 tmp = (unsigned __int64)t * (unsigned __int64)u; + + if(tmp > (unsigned __int64)IntTraits< T >::maxInt) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +// T = left arg and return type +// U = right arg +template < typename T, typename U > class LargeIntRegMultiply; + +#if SAFEINT_USE_INTRINSICS +// As usual, unsigned is easy +inline bool IntrinsicMultiplyUint64( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64* pRet ) SAFEINT_NOTHROW +{ + unsigned __int64 ulHigh = 0; + *pRet = _umul128(a , b, &ulHigh); + return ulHigh == 0; +} + +// Signed, is not so easy +inline bool IntrinsicMultiplyInt64( const signed __int64& a, const signed __int64& b, signed __int64* pRet ) SAFEINT_NOTHROW +{ + __int64 llHigh = 0; + *pRet = _mul128(a , b, &llHigh); + + // Now we need to figure out what we expect + // If llHigh is 0, then treat *pRet as unsigned + // If llHigh is < 0, then treat *pRet as signed + + if( (a ^ b) < 0 ) + { + // Negative result expected + if( llHigh == -1 && *pRet < 0 || + llHigh == 0 && *pRet == 0 ) + { + // Everything is within range + return true; + } + } + else + { + // Result should be positive + // Check for overflow + if( llHigh == 0 && (unsigned __int64)*pRet <= IntTraits< signed __int64 >::maxInt ) + return true; + } + return false; +} + +#endif + +template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int64 > +{ +public: + static bool RegMultiply( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, b, pRet ); +#else + unsigned __int32 aHigh, aLow, bHigh, bLow; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // Note - same approach applies for 128 bit math on a 64-bit system + + aHigh = (unsigned __int32)(a >> 32); + aLow = (unsigned __int32)a; + bHigh = (unsigned __int32)(b >> 32); + bLow = (unsigned __int32)b; + + *pRet = 0; + + if(aHigh == 0) + { + if(bHigh != 0) + { + *pRet = (unsigned __int64)aLow * (unsigned __int64)bHigh; + } + } + else if(bHigh == 0) + { + if(aHigh != 0) + { + *pRet = (unsigned __int64)aHigh * (unsigned __int64)bLow; + } + } + else + { + return false; + } + + if(*pRet != 0) + { + unsigned __int64 tmp; + + if((unsigned __int32)(*pRet >> 32) != 0) + return false; + + *pRet <<= 32; + tmp = (unsigned __int64)aLow * (unsigned __int64)bLow; + *pRet += tmp; + + if(*pRet < tmp) + return false; + + return true; + } + + *pRet = (unsigned __int64)aLow * (unsigned __int64)bLow; + return true; +#endif + } + + template < typename E > + static void RegMultiplyThrow( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, b, pRet ) ) + E::SafeIntOnOverflow(); +#else + unsigned __int32 aHigh, aLow, bHigh, bLow; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // Note - same approach applies for 128 bit math on a 64-bit system + + aHigh = (unsigned __int32)(a >> 32); + aLow = (unsigned __int32)a; + bHigh = (unsigned __int32)(b >> 32); + bLow = (unsigned __int32)b; + + *pRet = 0; + + if(aHigh == 0) + { + if(bHigh != 0) + { + *pRet = (unsigned __int64)aLow * (unsigned __int64)bHigh; + } + } + else if(bHigh == 0) + { + if(aHigh != 0) + { + *pRet = (unsigned __int64)aHigh * (unsigned __int64)bLow; + } + } + else + { + E::SafeIntOnOverflow(); + } + + if(*pRet != 0) + { + unsigned __int64 tmp; + + if((unsigned __int32)(*pRet >> 32) != 0) + E::SafeIntOnOverflow(); + + *pRet <<= 32; + tmp = (unsigned __int64)aLow * (unsigned __int64)bLow; + *pRet += tmp; + + if(*pRet < tmp) + E::SafeIntOnOverflow(); + + return; + } + + *pRet = (unsigned __int64)aLow * (unsigned __int64)bLow; +#endif + } +}; + +template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int32 > +{ +public: + static bool RegMultiply( const unsigned __int64& a, unsigned __int32 b, unsigned __int64* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ); +#else + unsigned __int32 aHigh, aLow; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * b + // => (aHigh * b * 2^32) + (aLow * b) + + aHigh = (unsigned __int32)(a >> 32); + aLow = (unsigned __int32)a; + + *pRet = 0; + + if(aHigh != 0) + { + *pRet = (unsigned __int64)aHigh * (unsigned __int64)b; + + unsigned __int64 tmp; + + if((unsigned __int32)(*pRet >> 32) != 0) + return false; + + *pRet <<= 32; + tmp = (unsigned __int64)aLow * (unsigned __int64)b; + *pRet += tmp; + + if(*pRet < tmp) + return false; + + return true; + } + + *pRet = (unsigned __int64)aLow * (unsigned __int64)b; + return true; +#endif + } + + template < typename E > + static void RegMultiplyThrow( const unsigned __int64& a, unsigned __int32 b, unsigned __int64* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + unsigned __int32 aHigh, aLow; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * b + // => (aHigh * b * 2^32) + (aLow * b) + + aHigh = (unsigned __int32)(a >> 32); + aLow = (unsigned __int32)a; + + *pRet = 0; + + if(aHigh != 0) + { + *pRet = (unsigned __int64)aHigh * (unsigned __int64)b; + + unsigned __int64 tmp; + + if((unsigned __int32)(*pRet >> 32) != 0) + E::SafeIntOnOverflow(); + + *pRet <<= 32; + tmp = (unsigned __int64)aLow * (unsigned __int64)b; + *pRet += tmp; + + if(*pRet < tmp) + E::SafeIntOnOverflow(); + + return; + } + + *pRet = (unsigned __int64)aLow * (unsigned __int64)b; + return; +#endif + } +}; + +template<> class LargeIntRegMultiply< unsigned __int64, signed __int32 > +{ +public: + // Intrinsic not needed + static bool RegMultiply( const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet ) SAFEINT_NOTHROW + { + if( b < 0 && a != 0 ) + return false; + +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ); +#else + return LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply(a, (unsigned __int32)b, pRet); +#endif + } + + template < typename E > + static void RegMultiplyThrow( const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet ) SAFEINT_CPP_THROW + { + if( b < 0 && a != 0 ) + E::SafeIntOnOverflow(); + +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( a, (unsigned __int32)b, pRet ); +#endif + } +}; + +template<> class LargeIntRegMultiply< unsigned __int64, signed __int64 > +{ +public: + static bool RegMultiply( const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet ) SAFEINT_NOTHROW + { + if( b < 0 && a != 0 ) + return false; + +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ); +#else + return LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply(a, (unsigned __int64)b, pRet); +#endif + } + + template < typename E > + static void RegMultiplyThrow( const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet ) SAFEINT_CPP_THROW + { + if( b < 0 && a != 0 ) + E::SafeIntOnOverflow(); + +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::template RegMultiplyThrow< E >( a, (unsigned __int64)b, pRet ); +#endif + } +}; + +template<> class LargeIntRegMultiply< signed __int32, unsigned __int64 > +{ +public: + // Devolves into ordinary 64-bit calculation + static bool RegMultiply( signed __int32 a, const unsigned __int64& b, signed __int32* pRet ) SAFEINT_NOTHROW + { + unsigned __int32 bHigh, bLow; + bool fIsNegative = false; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // aHigh == 0 implies: + // ( aLow * bHigh * 2^32 ) + ( aLow + bLow ) + // If the first part is != 0, fail + + bHigh = (unsigned __int32)(b >> 32); + bLow = (unsigned __int32)b; + + *pRet = 0; + + if(bHigh != 0 && a != 0) + return false; + + if( a < 0 ) + { + + a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); + fIsNegative = true; + } + + unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow; + + if( !fIsNegative ) + { + if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt ) + { + *pRet = (signed __int32)tmp; + return true; + } + } + else + { + if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt+1 ) + { + *pRet = SignedNegation< signed __int32 >::Value( tmp ); + return true; + } + } + + return false; + } + + template < typename E > + static void RegMultiplyThrow( signed __int32 a, const unsigned __int64& b, signed __int32* pRet ) SAFEINT_CPP_THROW + { + unsigned __int32 bHigh, bLow; + bool fIsNegative = false; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + + bHigh = (unsigned __int32)(b >> 32); + bLow = (unsigned __int32)b; + + *pRet = 0; + + if(bHigh != 0 && a != 0) + E::SafeIntOnOverflow(); + + if( a < 0 ) + { + a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); + fIsNegative = true; + } + + unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow; + + if( !fIsNegative ) + { + if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt ) + { + *pRet = (signed __int32)tmp; + return; + } + } + else + { + if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt+1 ) + { + *pRet = SignedNegation< signed __int32 >::Value( tmp ); + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template<> class LargeIntRegMultiply< unsigned __int32, unsigned __int64 > +{ +public: + // Becomes ordinary 64-bit multiplication, intrinsic not needed + static bool RegMultiply( unsigned __int32 a, const unsigned __int64& b, unsigned __int32* pRet ) SAFEINT_NOTHROW + { + // Consider that a*b can be broken up into: + // (bHigh * 2^32 + bLow) * a + // => (bHigh * a * 2^32) + (bLow * a) + // In this case, the result must fit into 32-bits + // If bHigh != 0 && a != 0, immediate error. + + if( (unsigned __int32)(b >> 32) != 0 && a != 0 ) + return false; + + unsigned __int64 tmp = b * (unsigned __int64)a; + + if( (unsigned __int32)(tmp >> 32) != 0 ) // overflow + return false; + + *pRet = (unsigned __int32)tmp; + return true; + } + + template < typename E > + static void RegMultiplyThrow( unsigned __int32 a, const unsigned __int64& b, unsigned __int32* pRet ) SAFEINT_CPP_THROW + { + if( (unsigned __int32)(b >> 32) != 0 && a != 0 ) + E::SafeIntOnOverflow(); + + unsigned __int64 tmp = b * (unsigned __int64)a; + + if( (unsigned __int32)(tmp >> 32) != 0 ) // overflow + E::SafeIntOnOverflow(); + + *pRet = (unsigned __int32)tmp; + } +}; + +template<> class LargeIntRegMultiply< unsigned __int32, signed __int64 > +{ +public: + static bool RegMultiply( unsigned __int32 a, const signed __int64& b, unsigned __int32* pRet ) SAFEINT_NOTHROW + { + if( b < 0 && a != 0 ) + return false; + return LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::RegMultiply( a, (unsigned __int64)b, pRet ); + } + + template < typename E > + static void RegMultiplyThrow( unsigned __int32 a, const signed __int64& b, unsigned __int32* pRet ) SAFEINT_CPP_THROW + { + if( b < 0 && a != 0 ) + E::SafeIntOnOverflow(); + + LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::template RegMultiplyThrow< E >( a, (unsigned __int64)b, pRet ); + } +}; + +template<> class LargeIntRegMultiply< signed __int64, signed __int64 > +{ +public: + static bool RegMultiply( const signed __int64& a, const signed __int64& b, signed __int64* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyInt64( a, b, pRet ); +#else + bool aNegative = false; + bool bNegative = false; + + unsigned __int64 tmp; + __int64 a1 = a; + __int64 b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( (unsigned __int64)a1, (unsigned __int64)b1, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + static void RegMultiplyThrow( const signed __int64& a, const signed __int64& b, signed __int64* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyInt64( a, b, pRet ) ) + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + bool bNegative = false; + + unsigned __int64 tmp; + __int64 a1 = a; + __int64 b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); + } + + LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::template RegMultiplyThrow< E >( (unsigned __int64)a1, (unsigned __int64)b1, &tmp ); + + // The unsigned multiplication didn't overflow or we'd be in the exception handler + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< signed __int64, unsigned __int32 > +{ +public: + static bool RegMultiply( const signed __int64& a, unsigned __int32 b, signed __int64* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyInt64( a, (signed __int64)b, pRet ); +#else + bool aNegative = false; + unsigned __int64 tmp; + __int64 a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply( (unsigned __int64)a1, b, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + static void RegMultiplyThrow( const signed __int64& a, unsigned __int32 b, signed __int64* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyInt64( a, (signed __int64)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + unsigned __int64 tmp; + __int64 a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( (unsigned __int64)a1, b, &tmp ); + + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< signed __int64, signed __int32 > +{ +public: + static bool RegMultiply( const signed __int64& a, signed __int32 b, signed __int64* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyInt64( a, (signed __int64)b, pRet ); +#else + bool aNegative = false; + bool bNegative = false; + + unsigned __int64 tmp; + __int64 a1 = a; + __int64 b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply( (unsigned __int64)a1, (unsigned __int32)b1, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + static void RegMultiplyThrow( signed __int64 a, signed __int32 b, signed __int64* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyInt64( a, (signed __int64)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + bool bNegative = false; + + unsigned __int64 tmp; + + if( a < 0 ) + { + aNegative = true; + a = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a); + } + + if( b < 0 ) + { + bNegative = true; + b = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(b); + } + + LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( (unsigned __int64)a, (unsigned __int32)b, &tmp ); + + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< signed __int32, signed __int64 > +{ +public: + static bool RegMultiply( signed __int32 a, const signed __int64& b, signed __int32* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + __int64 tmp; + + if( IntrinsicMultiplyInt64( a, b, &tmp ) ) + { + if( tmp > IntTraits< signed __int32 >::maxInt || + tmp < IntTraits< signed __int32 >::minInt ) + { + return false; + } + + *pRet = (__int32)tmp; + return true; + } + return false; +#else + bool aNegative = false; + bool bNegative = false; + + unsigned __int32 tmp; + __int64 b1 = b; + + if( a < 0 ) + { + aNegative = true; + a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); + } + + if( LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::RegMultiply( (unsigned __int32)a, (unsigned __int64)b1, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::minInt ) + { + *pRet = SignedNegation< signed __int32 >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::maxInt ) + { + *pRet = (signed __int32)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + static void RegMultiplyThrow( signed __int32 a, const signed __int64& b, signed __int32* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + __int64 tmp; + + if( IntrinsicMultiplyInt64( a, b, &tmp ) ) + { + if( tmp > IntTraits< signed __int32 >::maxInt || + tmp < IntTraits< signed __int32 >::minInt ) + { + E::SafeIntOnOverflow(); + } + + *pRet = (__int32)tmp; + return; + } + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + bool bNegative = false; + + unsigned __int32 tmp; + signed __int64 b2 = b; + + if( a < 0 ) + { + aNegative = true; + a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); + } + + if( b < 0 ) + { + bNegative = true; + b2 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b2); + } + + LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::template RegMultiplyThrow< E >( (unsigned __int32)a, (unsigned __int64)b2, &tmp ); + + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::minInt ) + { + *pRet = SignedNegation< signed __int32 >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::maxInt ) + { + *pRet = (signed __int32)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< signed __int64, unsigned __int64 > +{ +public: + // Leave this one as-is - will call unsigned intrinsic internally + static bool RegMultiply( const signed __int64& a, const unsigned __int64& b, signed __int64* pRet ) SAFEINT_NOTHROW + { + bool aNegative = false; + + unsigned __int64 tmp; + __int64 a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( (unsigned __int64)a1, (unsigned __int64)b, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return true; + } + } + } + + return false; + } + + template < typename E > + static void RegMultiplyThrow( const signed __int64& a, const unsigned __int64& b, signed __int64* pRet ) SAFEINT_CPP_THROW + { + bool aNegative = false; + unsigned __int64 tmp; + __int64 a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( (unsigned __int64)a1, (unsigned __int64)b, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return; + } + } + } + + E::SafeIntOnOverflow(); + } +}; + +// In all of the following functions where LargeIntRegMultiply methods are called, +// we need to properly transition types. The methods need __int64, __int32, etc. +// but the variables being passed to us could be long long, long int, or long, depending on +// the compiler. Microsoft compiler knows that long long is the same type as __int64, but gcc doesn't + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Uint64 > +{ +public: + // T, U are unsigned __int64 + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isUint64 && IntTraits::isUint64 ); + unsigned __int64 t1 = t; + unsigned __int64 u1 = u; + return LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( t1, u1, reinterpret_cast(&ret) ); + } + + template < typename E > + static void MultiplyThrow(const unsigned __int64& t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isUint64 && IntTraits::isUint64 ); + unsigned __int64 t1 = t; + unsigned __int64 u1 = u; + LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::template RegMultiplyThrow< E >( t1, u1, reinterpret_cast(&ret) ); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Uint > +{ +public: + // T is unsigned __int64 + // U is any unsigned int 32-bit or less + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 t1 = t; + return LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply( t1, (unsigned __int32)u, reinterpret_cast(&ret) ); + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 t1 = t; + LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( t1, (unsigned __int32)u, reinterpret_cast(&ret) ); + } +}; + +// converse of the previous function +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_UintUint64 > +{ +public: + // T is any unsigned int up to 32-bit + // U is unsigned __int64 + static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 u1 = u; + unsigned __int32 tmp; + + if( LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::RegMultiply( t, u1, &tmp ) && + SafeCastHelper< T, unsigned __int32, GetCastMethod< T, unsigned __int32 >::method >::Cast(tmp, ret) ) + { + return true; + } + + return false; + } + + template < typename E > + static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 u1 = u; + unsigned __int32 tmp; + + LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::template RegMultiplyThrow< E >( t, u1, &tmp ); + SafeCastHelper< T, unsigned __int32, GetCastMethod< T, unsigned __int32 >::method >::template CastThrow< E >(tmp, ret); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Int > +{ +public: + // T is unsigned __int64 + // U is any signed int, up to 64-bit + static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 t1 = t; + return LargeIntRegMultiply< unsigned __int64, signed __int32 >::RegMultiply(t1, (signed __int32)u, reinterpret_cast< unsigned __int64* >(&ret)); + } + + template < typename E > + static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 t1 = t; + LargeIntRegMultiply< unsigned __int64, signed __int32 >::template RegMultiplyThrow< E >(t1, (signed __int32)u, reinterpret_cast< unsigned __int64* >(&ret)); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Int64 > +{ +public: + // T is unsigned __int64 + // U is __int64 + static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isUint64 && IntTraits::isInt64 ); + unsigned __int64 t1 = t; + __int64 u1 = u; + return LargeIntRegMultiply< unsigned __int64, __int64 >::RegMultiply(t1, u1, reinterpret_cast< unsigned __int64* >(&ret)); + } + + template < typename E > + static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isUint64 && IntTraits::isInt64 ); + unsigned __int64 t1 = t; + __int64 u1 = u; + LargeIntRegMultiply< unsigned __int64, __int64 >::template RegMultiplyThrow< E >(t1, u1, reinterpret_cast< unsigned __int64* >(&ret)); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_UintInt64 > +{ +public: + // T is unsigned up to 32-bit + // U is __int64 + static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 u1 = u; + unsigned __int32 tmp; + + if( LargeIntRegMultiply< unsigned __int32, __int64 >::RegMultiply( (unsigned __int32)t, u1, &tmp ) && + SafeCastHelper< T, unsigned __int32, GetCastMethod< T, unsigned __int32 >::method >::Cast(tmp, ret) ) + { + return true; + } + + return false; + } + + template < typename E > + static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 u1 = u; + unsigned __int32 tmp; + + LargeIntRegMultiply< unsigned __int32, __int64 >::template RegMultiplyThrow< E >( (unsigned __int32)t, u1, &tmp ); + SafeCastHelper< T, unsigned __int32, GetCastMethod< T, unsigned __int32 >::method >::template CastThrow< E >(tmp, ret); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Uint > +{ +public: + // T is __int64 + // U is unsigned up to 32-bit + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 t1 = t; + return LargeIntRegMultiply< __int64, unsigned __int32 >::RegMultiply( t1, (unsigned __int32)u, reinterpret_cast< __int64* >(&ret) ); + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 t1 = t; + LargeIntRegMultiply< __int64, unsigned __int32 >::template RegMultiplyThrow< E >( t1, (unsigned __int32)u, reinterpret_cast< __int64* >(&ret) ); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Int64 > +{ +public: + // T, U are __int64 + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isInt64 && IntTraits::isInt64 ); + __int64 t1 = t; + __int64 u1 = u; + return LargeIntRegMultiply< __int64, __int64 >::RegMultiply( t1, u1, reinterpret_cast< __int64* >(&ret) ); + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isInt64 && IntTraits::isInt64 ); + __int64 t1 = t; + __int64 u1 = u; + LargeIntRegMultiply< __int64, __int64 >::template RegMultiplyThrow< E >( t1, u1, reinterpret_cast< __int64* >(&ret)); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Int > +{ +public: + // T is __int64 + // U is signed up to 32-bit + static bool Multiply( const T& t, U u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 t1 = t; + return LargeIntRegMultiply< __int64, __int32 >::RegMultiply( t1, (__int32)u, reinterpret_cast< __int64* >(&ret)); + } + + template < typename E > + static void MultiplyThrow( const __int64& t, U u, T& ret ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 t1 = t; + LargeIntRegMultiply< __int64, __int32 >::template RegMultiplyThrow< E >(t1, (__int32)u, reinterpret_cast< __int64* >(&ret)); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_IntUint64 > +{ +public: + // T is signed up to 32-bit + // U is unsigned __int64 + static bool Multiply(T t, const U& u, T& ret) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 u1 = u; + __int32 tmp; + + if( LargeIntRegMultiply< __int32, unsigned __int64 >::RegMultiply( (__int32)t, u1, &tmp ) && + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( tmp, ret ) ) + { + return true; + } + + return false; + } + + template < typename E > + static void MultiplyThrow(T t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 u1 = u; + __int32 tmp; + + LargeIntRegMultiply< __int32, unsigned __int64 >::template RegMultiplyThrow< E >( (__int32)t, u1, &tmp ); + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( tmp, ret ); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Uint64> +{ +public: + // T is __int64 + // U is unsigned __int64 + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isInt64 && IntTraits::isUint64 ); + __int64 t1 = t; + unsigned __int64 u1 = u; + return LargeIntRegMultiply< __int64, unsigned __int64 >::RegMultiply( t1, u1, reinterpret_cast< __int64* >(&ret) ); + } + + template < typename E > + static void MultiplyThrow( const __int64& t, const unsigned __int64& u, T& ret ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isInt64 && IntTraits::isUint64 ); + __int64 t1 = t; + unsigned __int64 u1 = u; + LargeIntRegMultiply< __int64, unsigned __int64 >::template RegMultiplyThrow< E >( t1, u1, reinterpret_cast< __int64* >(&ret) ); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_IntInt64> +{ +public: + // T is signed, up to 32-bit + // U is __int64 + static bool Multiply( T t, const U& u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 u1 = u; + __int32 tmp; + + if( LargeIntRegMultiply< __int32, __int64 >::RegMultiply( (__int32)t, u1, &tmp ) && + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( tmp, ret ) ) + { + return true; + } + + return false; + } + + template < typename E > + static void MultiplyThrow(T t, const U& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 u1 = u; + __int32 tmp; + + LargeIntRegMultiply< __int32, __int64 >::template RegMultiplyThrow< E >( (__int32)t, u1, &tmp ); + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( tmp, ret ); + } +}; + +enum DivisionState +{ + DivisionState_OK, + DivisionState_UnsignedSigned, + DivisionState_SignedUnsigned32, + DivisionState_SignedUnsigned64, + DivisionState_SignedUnsigned, + DivisionState_SignedSigned +}; + +template < typename T, typename U > class DivisionMethod +{ +public: + enum + { + method = (SafeIntCompare< T, U >::isBothUnsigned ? DivisionState_OK : + (!IntTraits< T >::isSigned && IntTraits< U >::isSigned) ? DivisionState_UnsignedSigned : + (IntTraits< T >::isSigned && + IntTraits< U >::isUint32 && + IntTraits< T >::isLT64Bit) ? DivisionState_SignedUnsigned32 : + (IntTraits< T >::isSigned && IntTraits< U >::isUint64) ? DivisionState_SignedUnsigned64 : + (IntTraits< T >::isSigned && !IntTraits< U >::isSigned) ? DivisionState_SignedUnsigned : + DivisionState_SignedSigned) + }; +}; + +template < typename T, typename U, int state > class DivisionHelper; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_OK > +{ +public: + static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + return SafeIntDivideByZero; + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + result = (T)( t/u ); + return SafeIntNoError; + } + + template < typename E > + static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if( u == 0 ) + E::SafeIntOnDivZero(); + + if( t == 0 ) + { + result = 0; + return; + } + + result = (T)( t/u ); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_UnsignedSigned> +{ +public: + static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + + if( u == 0 ) + return SafeIntDivideByZero; + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + if( u > 0 ) + { + result = (T)( t/u ); + return SafeIntNoError; + } + + // it is always an error to try and divide an unsigned number by a negative signed number + // unless u is bigger than t + if( AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u ) > t ) + { + result = 0; + return SafeIntNoError; + } + + return SafeIntArithmeticOverflow; + } + + template < typename E > + static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + + if( u == 0 ) + E::SafeIntOnDivZero(); + + if( t == 0 ) + { + result = 0; + return; + } + + if( u > 0 ) + { + result = (T)( t/u ); + return; + } + + // it is always an error to try and divide an unsigned number by a negative signed number + // unless u is bigger than t + if( AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u ) > t ) + { + result = 0; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned32 > +{ +public: + static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + return SafeIntDivideByZero; + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + // Test for t > 0 + // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors + // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional + + if( t > 0 ) + result = (T)( t/u ); + else + result = (T)( (__int64)t/(__int64)u ); + + return SafeIntNoError; + } + + template < typename E > + static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + // Test for t > 0 + // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors + // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional + + if( t > 0 ) + result = (T)( t/u ); + else + result = (T)( (__int64)t/(__int64)u ); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned64 > +{ +public: + static SafeIntError Divide( const T& t, const unsigned __int64& u, T& result ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits< U >::isUint64 ); + + if( u == 0 ) + { + return SafeIntDivideByZero; + } + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + if( u <= (unsigned __int64)IntTraits< T >::maxInt ) + { + // Else u can safely be cast to T + if( CompileConst< sizeof( T ) < sizeof( __int64 )>::Value() ) + result = (T)( (int)t/(int)u ); + else + result = (T)((__int64)t/(__int64)u); + } + else // Corner case + if( t == IntTraits< T >::minInt && u == (unsigned __int64)IntTraits< T >::minInt ) + { + // Min int divided by it's own magnitude is -1 + result = -1; + } + else + { + result = 0; + } + return SafeIntNoError; + } + + template < typename E > + static void DivideThrow( const T& t, const unsigned __int64& u, T& result ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits< U >::isUint64 ); + + if( u == 0 ) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + if( u <= (unsigned __int64)IntTraits< T >::maxInt ) + { + // Else u can safely be cast to T + if( CompileConst< sizeof( T ) < sizeof( __int64 ) >::Value() ) + result = (T)( (int)t/(int)u ); + else + result = (T)((__int64)t/(__int64)u); + } + else // Corner case + if( t == IntTraits< T >::minInt && u == (unsigned __int64)IntTraits< T >::minInt ) + { + // Min int divided by it's own magnitude is -1 + result = -1; + } + else + { + result = 0; + } + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned> +{ +public: + // T is any signed, U is unsigned and smaller than 32-bit + // In this case, standard operator casting is correct + static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + { + return SafeIntDivideByZero; + } + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + result = (T)( t/u ); + return SafeIntNoError; + } + + template < typename E > + static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + result = (T)( t/u ); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedSigned> +{ +public: + static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + { + return SafeIntDivideByZero; + } + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + // Must test for corner case + if( t == IntTraits< T >::minInt && u == (U)-1 ) + return SafeIntArithmeticOverflow; + + result = (T)( t/u ); + return SafeIntNoError; + } + + template < typename E > + static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + // Must test for corner case + if( t == IntTraits< T >::minInt && u == (U)-1 ) + E::SafeIntOnOverflow(); + + result = (T)( t/u ); + } +}; + +enum AdditionState +{ + AdditionState_CastIntCheckMax, + AdditionState_CastUintCheckOverflow, + AdditionState_CastUintCheckOverflowMax, + AdditionState_CastUint64CheckOverflow, + AdditionState_CastUint64CheckOverflowMax, + AdditionState_CastIntCheckSafeIntMinMax, + AdditionState_CastInt64CheckSafeIntMinMax, + AdditionState_CastInt64CheckMax, + AdditionState_CastUint64CheckSafeIntMinMax, + AdditionState_CastUint64CheckSafeIntMinMax2, + AdditionState_CastInt64CheckOverflow, + AdditionState_CastInt64CheckOverflowSafeIntMinMax, + AdditionState_CastInt64CheckOverflowMax, + AdditionState_ManualCheckInt64Uint64, + AdditionState_ManualCheck, + AdditionState_Error +}; + +template< typename T, typename U > +class AdditionMethod +{ +public: + enum + { + //unsigned-unsigned + method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? AdditionState_CastIntCheckMax : + (IntRegion< T,U >::IntZone_Uint32_UintLT64) ? AdditionState_CastUintCheckOverflow : + (IntRegion< T,U >::IntZone_UintLT32_Uint32) ? AdditionState_CastUintCheckOverflowMax : + (IntRegion< T,U >::IntZone_Uint64_Uint) ? AdditionState_CastUint64CheckOverflow : + (IntRegion< T,U >::IntZone_UintLT64_Uint64) ? AdditionState_CastUint64CheckOverflowMax : + //unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? AdditionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? AdditionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? AdditionState_CastUint64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? AdditionState_CastUint64CheckSafeIntMinMax2 : + //signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? AdditionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? AdditionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? AdditionState_CastInt64CheckOverflow : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? AdditionState_CastInt64CheckOverflowSafeIntMinMax : + //signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? AdditionState_CastIntCheckMax : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? AdditionState_CastInt64CheckMax : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? AdditionState_CastInt64CheckOverflowMax : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? AdditionState_ManualCheckInt64Uint64 : + (IntRegion< T,U >::IntZone_Int_Uint64) ? AdditionState_ManualCheck : + AdditionState_Error) + }; +}; + +template < typename T, typename U, int method > class AdditionHelper; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastIntCheckMax > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //16-bit or less unsigned addition + __int32 tmp = lhs + rhs; + + if( tmp <= (__int32)IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //16-bit or less unsigned addition + __int32 tmp = lhs + rhs; + + if( tmp <= (__int32)IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUintCheckOverflow > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - both are unsigned + unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; + + //we added didn't get smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return true; + } + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 32-bit or less - both are unsigned + unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; + + //we added didn't get smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return; + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUintCheckOverflowMax> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - both are unsigned + unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; + + // We added and it didn't get smaller or exceed maxInt + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //32-bit or less - both are unsigned + unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; + + // We added and it didn't get smaller or exceed maxInt + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckOverflow> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs unsigned __int64, rhs unsigned + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it didn't get smaller + if(tmp >= lhs) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs unsigned __int64, rhs unsigned + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it didn't get smaller + if(tmp >= lhs) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckOverflowMax > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //lhs unsigned __int64, rhs unsigned + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it didn't get smaller + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //lhs unsigned __int64, rhs unsigned + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it didn't get smaller + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastIntCheckSafeIntMinMax > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 16-bit or less - one or both are signed + __int32 tmp = lhs + rhs; + + if( tmp <= (__int32)IntTraits< T >::maxInt && tmp >= (__int32)IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 16-bit or less - one or both are signed + __int32 tmp = lhs + rhs; + + if( tmp <= (__int32)IntTraits< T >::maxInt && tmp >= (__int32)IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckSafeIntMinMax > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - one or both are signed + __int64 tmp = (__int64)lhs + (__int64)rhs; + + if( tmp <= (__int64)IntTraits< T >::maxInt && tmp >= (__int64)IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 32-bit or less - one or both are signed + __int64 tmp = (__int64)lhs + (__int64)rhs; + + if( tmp <= (__int64)IntTraits< T >::maxInt && tmp >= (__int64)IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckMax > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - lhs signed, rhs unsigned + __int64 tmp = (__int64)lhs + (__int64)rhs; + + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 32-bit or less - lhs signed, rhs unsigned + __int64 tmp = (__int64)lhs + (__int64)rhs; + + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckSafeIntMinMax > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is unsigned __int64, rhs signed + unsigned __int64 tmp; + + if( rhs < 0 ) + { + // So we're effectively subtracting + tmp = AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if( tmp <= lhs ) + { + result = lhs - tmp; + return true; + } + } + else + { + // now we know that rhs can be safely cast into an unsigned __int64 + tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it did not become smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is unsigned __int64, rhs signed + unsigned __int64 tmp; + + if( rhs < 0 ) + { + // So we're effectively subtracting + tmp = AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if( tmp <= lhs ) + { + result = lhs - tmp; + return; + } + } + else + { + // now we know that rhs can be safely cast into an unsigned __int64 + tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it did not become smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckSafeIntMinMax2> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is unsigned and < 64-bit, rhs signed __int64 + if( rhs < 0 ) + { + if( lhs >= ~(unsigned __int64)( rhs ) + 1 )//negation is safe, since rhs is 64-bit + { + result = (T)( lhs + rhs ); + return true; + } + } + else + { + // now we know that rhs can be safely cast into an unsigned __int64 + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff + // it is not possible for the operation above to overflow, so just check max + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + } + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is unsigned and < 64-bit, rhs signed __int64 + if( rhs < 0 ) + { + if( lhs >= ~(unsigned __int64)( rhs ) + 1) //negation is safe, since rhs is 64-bit + { + result = (T)( lhs + rhs ); + return; + } + } + else + { + // now we know that rhs can be safely cast into an unsigned __int64 + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff + // it is not possible for the operation above to overflow, so just check max + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflow> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is signed __int64, rhs signed + __int64 tmp = (__int64)((unsigned __int64)lhs + (unsigned __int64)rhs); + + if( lhs >= 0 ) + { + // mixed sign cannot overflow + if( rhs >= 0 && tmp < lhs ) + return false; + } + else + { + // lhs negative + if( rhs < 0 && tmp > lhs ) + return false; + } + + result = (T)tmp; + return true; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is signed __int64, rhs signed + __int64 tmp = (__int64)((unsigned __int64)lhs + (unsigned __int64)rhs); + + if( lhs >= 0 ) + { + // mixed sign cannot overflow + if( rhs >= 0 && tmp < lhs ) + E::SafeIntOnOverflow(); + } + else + { + // lhs negative + if( rhs < 0 && tmp > lhs ) + E::SafeIntOnOverflow(); + } + + result = (T)tmp; + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflowSafeIntMinMax> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //rhs is signed __int64, lhs signed + __int64 tmp; + + if( AdditionHelper< __int64, __int64, AdditionState_CastInt64CheckOverflow >::Addition( (__int64)lhs, (__int64)rhs, tmp ) && + tmp <= IntTraits< T >::maxInt && + tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //rhs is signed __int64, lhs signed + __int64 tmp; + + AdditionHelper< __int64, __int64, AdditionState_CastInt64CheckOverflow >::AdditionThrow< E >( (__int64)lhs, (__int64)rhs, tmp ); + + if( tmp <= IntTraits< T >::maxInt && + tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflowMax> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //lhs is signed __int64, rhs unsigned < 64-bit + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + if( (__int64)tmp >= lhs ) + { + result = (T)(__int64)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is signed __int64, rhs unsigned < 64-bit + // Some compilers get optimization-happy, let's thwart them + + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + if( (__int64)tmp >= lhs ) + { + result = (T)(__int64)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_ManualCheckInt64Uint64 > +{ +public: + static bool Addition( const __int64& lhs, const unsigned __int64& rhs, __int64& result ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); + // rhs is unsigned __int64, lhs __int64 + // cast everything to unsigned, perform addition, then + // cast back for check - this is done to stop optimizers from removing the code + unsigned __int64 tmp = (unsigned __int64)lhs + rhs; + + if( (__int64)tmp >= lhs ) + { + result = (__int64)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); + // rhs is unsigned __int64, lhs __int64 + unsigned __int64 tmp = (unsigned __int64)lhs + rhs; + + if( (__int64)tmp >= lhs ) + { + result = (__int64)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_ManualCheck> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // rhs is unsigned __int64, lhs signed, 32-bit or less + if( (unsigned __int32)( rhs >> 32 ) == 0 ) + { + // Now it just happens to work out that the standard behavior does what we want + // Adding explicit casts to show exactly what's happening here + // Note - this is tweaked to keep optimizers from tossing out the code. + unsigned __int32 tmp = (unsigned __int32)rhs + (unsigned __int32)lhs; + + if( (__int32)tmp >= lhs && SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( (__int32)tmp, result ) ) + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // rhs is unsigned __int64, lhs signed, 32-bit or less + + if( (unsigned __int32)( rhs >> 32 ) == 0 ) + { + // Now it just happens to work out that the standard behavior does what we want + // Adding explicit casts to show exactly what's happening here + unsigned __int32 tmp = (unsigned __int32)rhs + (unsigned __int32)lhs; + + if( (__int32)tmp >= lhs ) + { + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( (__int32)tmp, result ); + return; + } + } + E::SafeIntOnOverflow(); + } +}; + +enum SubtractionState +{ + SubtractionState_BothUnsigned, + SubtractionState_CastIntCheckSafeIntMinMax, + SubtractionState_CastIntCheckMin, + SubtractionState_CastInt64CheckSafeIntMinMax, + SubtractionState_CastInt64CheckMin, + SubtractionState_Uint64Int, + SubtractionState_UintInt64, + SubtractionState_Int64Int, + SubtractionState_IntInt64, + SubtractionState_Int64Uint, + SubtractionState_IntUint64, + SubtractionState_Int64Uint64, + // states for SubtractionMethod2 + SubtractionState_BothUnsigned2, + SubtractionState_CastIntCheckSafeIntMinMax2, + SubtractionState_CastInt64CheckSafeIntMinMax2, + SubtractionState_Uint64Int2, + SubtractionState_UintInt642, + SubtractionState_Int64Int2, + SubtractionState_IntInt642, + SubtractionState_Int64Uint2, + SubtractionState_IntUint642, + SubtractionState_Int64Uint642, + SubtractionState_Error +}; + +template < typename T, typename U > class SubtractionMethod +{ +public: + enum + { + // unsigned-unsigned + method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 || + (IntRegion< T,U >::IntZone_Uint32_UintLT64) || + (IntRegion< T,U >::IntZone_UintLT32_Uint32) || + (IntRegion< T,U >::IntZone_Uint64_Uint) || + (IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt64 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt64 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckMin : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckMin : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint : + (IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint64 : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint64 : + SubtractionState_Error) + }; +}; + +// this is for the case of U - SafeInt< T, E > +template < typename T, typename U > class SubtractionMethod2 +{ +public: + enum + { + // unsigned-unsigned + method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 || + (IntRegion< T,U >::IntZone_Uint32_UintLT64) || + (IntRegion< T,U >::IntZone_UintLT32_Uint32) || + (IntRegion< T,U >::IntZone_Uint64_Uint) || + (IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned2 : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int2 : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt642 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int2 : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt642 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint2 : + (IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint642 : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint642 : + SubtractionState_Error) + }; +}; + +template < typename T, typename U, int method > class SubtractionHelper; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_BothUnsigned > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + result = (T)( lhs - rhs ); + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + result = (T)( lhs - rhs ); + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_BothUnsigned2 > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, U& result ) SAFEINT_NOTHROW + { + // both are unsigned - easy case + // Except we do have to check for overflow - lhs could be larger than result can hold + if( rhs <= lhs ) + { + T tmp = (T)(lhs - rhs); + return SafeCastHelper< U, T, GetCastMethod::method>::Cast( tmp, result); + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, U& result ) SAFEINT_CPP_THROW + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + T tmp = (T)(lhs - rhs); + SafeCastHelper< U, T, GetCastMethod::method >::template CastThrow( tmp, result); + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastIntCheckSafeIntMinMax > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + __int32 tmp = lhs - rhs; + + if( SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( tmp, result ) ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + __int32 tmp = lhs - rhs; + + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template class SubtractionHelper< U, T, SubtractionState_CastIntCheckSafeIntMinMax2 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + __int32 tmp = lhs - rhs; + + return SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( tmp, result ); + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + __int32 tmp = lhs - rhs; + + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastIntCheckMin > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 16-bit or less + // rhs is unsigned - check only minimum + __int32 tmp = lhs - rhs; + + if( tmp >= (__int32)IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 16-bit or less + // rhs is unsigned - check only minimum + __int32 tmp = lhs - rhs; + + if( tmp >= (__int32)IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastInt64CheckSafeIntMinMax > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + __int64 tmp = (__int64)lhs - (__int64)rhs; + + return SafeCastHelper< T, __int64, GetCastMethod< T, __int64 >::method >::Cast( tmp, result ); + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + __int64 tmp = (__int64)lhs - (__int64)rhs; + + SafeCastHelper< T, __int64, GetCastMethod< T, __int64 >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template class SubtractionHelper< U, T, SubtractionState_CastInt64CheckSafeIntMinMax2 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + __int64 tmp = (__int64)lhs - (__int64)rhs; + + return SafeCastHelper< T, __int64, GetCastMethod< T, __int64 >::method >::Cast( tmp, result ); + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + __int64 tmp = (__int64)lhs - (__int64)rhs; + + SafeCastHelper< T, __int64, GetCastMethod< T, __int64 >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastInt64CheckMin > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 32-bit or less + // rhs is unsigned - check only minimum + __int64 tmp = (__int64)lhs - (__int64)rhs; + + if( tmp >= (__int64)IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 32-bit or less + // rhs is unsigned - check only minimum + __int64 tmp = (__int64)lhs - (__int64)rhs; + + if( tmp >= (__int64)IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Uint64Int > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is an unsigned __int64, rhs signed + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (unsigned __int64)rhs <= lhs ) + { + result = (T)( lhs - (unsigned __int64)rhs ); + return true; + } + } + else + { + T tmp = lhs; + // we're now effectively adding + result = lhs + AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if(result >= tmp) + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is an unsigned __int64, rhs signed + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (unsigned __int64)rhs <= lhs ) + { + result = (T)( lhs - (unsigned __int64)rhs ); + return; + } + } + else + { + T tmp = lhs; + // we're now effectively adding + result = lhs + AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if(result >= tmp) + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Uint64Int2 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // U is unsigned __int64, T is signed + if( rhs < 0 ) + { + // treat this as addition + unsigned __int64 tmp; + + tmp = lhs + (unsigned __int64)AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( rhs ); + + // must check for addition overflow and max + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + } + else if( (unsigned __int64)rhs > lhs ) // now both are positive, so comparison always works + { + // result is negative + // implies that lhs must fit into T, and result cannot overflow + // Also allows us to drop to 32-bit math, which is faster on a 32-bit system + result = (T)lhs - (T)rhs; + return true; + } + else + { + // result is positive + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // U is unsigned __int64, T is signed + if( rhs < 0 ) + { + // treat this as addition + unsigned __int64 tmp; + + tmp = lhs + (unsigned __int64)AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( rhs ); + + // must check for addition overflow and max + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + } + else if( (unsigned __int64)rhs > lhs ) // now both are positive, so comparison always works + { + // result is negative + // implies that lhs must fit into T, and result cannot overflow + // Also allows us to drop to 32-bit math, which is faster on a 32-bit system + result = (T)lhs - (T)rhs; + return; + } + else + { + // result is positive + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_UintInt64 > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is an unsigned int32 or smaller, rhs signed __int64 + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (unsigned __int64)rhs <= lhs ) + { + result = (T)( lhs - (T)rhs ); + return true; + } + } + else + { + // we're now effectively adding + // since lhs is 32-bit, and rhs cannot exceed 2^63 + // this addition cannot overflow + unsigned __int64 tmp = lhs + ~(unsigned __int64)( rhs ) + 1; // negation safe + + // but we could exceed MaxInt + if(tmp <= IntTraits< T >::maxInt) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is an unsigned int32 or smaller, rhs signed __int64 + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (unsigned __int64)rhs <= lhs ) + { + result = (T)( lhs - (T)rhs ); + return; + } + } + else + { + // we're now effectively adding + // since lhs is 32-bit, and rhs cannot exceed 2^63 + // this addition cannot overflow + unsigned __int64 tmp = lhs + ~(unsigned __int64)( rhs ) + 1; // negation safe + + // but we could exceed MaxInt + if(tmp <= IntTraits< T >::maxInt) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template class SubtractionHelper< U, T, SubtractionState_UintInt642 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // U unsigned 32-bit or less, T __int64 + if( rhs >= 0 ) + { + // overflow not possible + result = (T)( (__int64)lhs - rhs ); + return true; + } + else + { + // we effectively have an addition + // which cannot overflow internally + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)( -rhs ); + + if( tmp <= (unsigned __int64)IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // U unsigned 32-bit or less, T __int64 + if( rhs >= 0 ) + { + // overflow not possible + result = (T)( (__int64)lhs - rhs ); + return; + } + else + { + // we effectively have an addition + // which cannot overflow internally + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)( -rhs ); + + if( tmp <= (unsigned __int64)IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Int > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is an __int64, rhs signed (up to 64-bit) + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible + + __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); + + // Note - ideally, we can order these so that true conditionals + // lead to success, which enables better pipelining + // It isn't practical here + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || // condition 2 + ( rhs >= 0 && tmp > lhs ) ) // condition 3 + { + return false; + } + + result = (T)tmp; + return true; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is an __int64, rhs signed (up to 64-bit) + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible + + __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); + + // Note - ideally, we can order these so that true conditionals + // lead to success, which enables better pipelining + // It isn't practical here + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || // condition 2 + ( rhs >= 0 && tmp > lhs ) ) // condition 3 + { + E::SafeIntOnOverflow(); + } + + result = (T)tmp; + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Int2 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs __int64, rhs any signed int (including __int64) + __int64 tmp = lhs - rhs; + + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible in tmp + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible in tmp + + if( lhs >= 0 ) + { + // if both positive, overflow to negative not possible + // which is why we'll explicitly check maxInt, and not call SafeCast + if( ( IntTraits< T >::isLT64Bit && tmp > IntTraits< T >::maxInt ) || + ( rhs < 0 && tmp < lhs ) ) + { + return false; + } + } + else + { + // lhs negative + if( ( IntTraits< T >::isLT64Bit && tmp < IntTraits< T >::minInt) || + ( rhs >=0 && tmp > lhs ) ) + { + return false; + } + } + + result = (T)tmp; + return true; + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs __int64, rhs any signed int (including __int64) + __int64 tmp = lhs - rhs; + + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible in tmp + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible in tmp + + if( lhs >= 0 ) + { + // if both positive, overflow to negative not possible + // which is why we'll explicitly check maxInt, and not call SafeCast + if( ( CompileConst< IntTraits< T >::isLT64Bit >::Value() && tmp > IntTraits< T >::maxInt ) || + ( rhs < 0 && tmp < lhs ) ) + { + E::SafeIntOnOverflow(); + } + } + else + { + // lhs negative + if( ( CompileConst< IntTraits< T >::isLT64Bit >::Value() && tmp < IntTraits< T >::minInt) || + ( rhs >=0 && tmp > lhs ) ) + { + E::SafeIntOnOverflow(); + } + } + + result = (T)tmp; + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_IntInt64 > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is a 32-bit int or less, rhs __int64 + // we have essentially 4 cases: + // + // lhs positive, rhs positive - rhs could be larger than lhs can represent + // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int + // lhs negative, rhs positive - check tmp <= lhs and tmp < min int + // lhs negative, rhs negative - addition cannot internally overflow, check against max + + __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); + + if( lhs >= 0 ) + { + // first case + if( rhs >= 0 ) + { + if( tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + } + else + { + // second case + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + } + } + else + { + // lhs < 0 + // third case + if( rhs >= 0 ) + { + if( tmp <= lhs && tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + } + else + { + // fourth case + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + } + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is a 32-bit int or less, rhs __int64 + // we have essentially 4 cases: + // + // lhs positive, rhs positive - rhs could be larger than lhs can represent + // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int + // lhs negative, rhs positive - check tmp <= lhs and tmp < min int + // lhs negative, rhs negative - addition cannot internally overflow, check against max + + __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); + + if( lhs >= 0 ) + { + // first case + if( rhs >= 0 ) + { + if( tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + } + else + { + // second case + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + } + } + else + { + // lhs < 0 + // third case + if( rhs >= 0 ) + { + if( tmp <= lhs && tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + } + else + { + // fourth case + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_IntInt642 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is any signed int32 or smaller, rhs is int64 + __int64 tmp = (__int64)lhs - rhs; + + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || + ( rhs > 0 && tmp > lhs ) ) + { + return false; + //else OK + } + + result = (T)tmp; + return true; + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is any signed int32 or smaller, rhs is int64 + __int64 tmp = (__int64)lhs - rhs; + + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || + ( rhs > 0 && tmp > lhs ) ) + { + E::SafeIntOnOverflow(); + //else OK + } + + result = (T)tmp; + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Uint > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is a 64-bit int, rhs unsigned int32 or smaller + // perform test as unsigned to prevent unwanted optimizations + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( (__int64)tmp <= lhs ) + { + result = (T)(__int64)tmp; + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is a 64-bit int, rhs unsigned int32 or smaller + // perform test as unsigned to prevent unwanted optimizations + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( (__int64)tmp <= lhs ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Uint2 > +{ +public: + // lhs is __int64, rhs is unsigned 32-bit or smaller + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // Do this as unsigned to prevent unwanted optimizations + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( (__int64)tmp <= IntTraits< T >::maxInt && (__int64)tmp >= IntTraits< T >::minInt ) + { + result = (T)(__int64)tmp; + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // Do this as unsigned to prevent unwanted optimizations + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( (__int64)tmp <= IntTraits< T >::maxInt && (__int64)tmp >= IntTraits< T >::minInt ) + { + result = (T)(__int64)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_IntUint64 > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is any signed int, rhs unsigned int64 + // check against available range + + // We need the absolute value of IntTraits< T >::minInt + // This will give it to us without extraneous compiler warnings + const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits< T >::maxInt + 1; + + if( lhs < 0 ) + { + if( rhs <= AbsMinIntT - AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( lhs ) ) + { + result = (T)( lhs - rhs ); + return true; + } + } + else + { + if( rhs <= AbsMinIntT + (unsigned __int64)lhs ) + { + result = (T)( lhs - rhs ); + return true; + } + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is any signed int, rhs unsigned int64 + // check against available range + + // We need the absolute value of IntTraits< T >::minInt + // This will give it to us without extraneous compiler warnings + const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits< T >::maxInt + 1; + + if( lhs < 0 ) + { + if( rhs <= AbsMinIntT - AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( lhs ) ) + { + result = (T)( lhs - rhs ); + return; + } + } + else + { + if( rhs <= AbsMinIntT + (unsigned __int64)lhs ) + { + result = (T)( lhs - rhs ); + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_IntUint642 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // We run into upcasting problems on comparison - needs 2 checks + if( lhs >= 0 && (T)lhs >= rhs ) + { + result = (T)((U)lhs - (U)rhs); + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // We run into upcasting problems on comparison - needs 2 checks + if( lhs >= 0 && (T)lhs >= rhs ) + { + result = (T)((U)lhs - (U)rhs); + return; + } + + E::SafeIntOnOverflow(); + } + +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Uint64 > +{ +public: + static bool Subtract( const __int64& lhs, const unsigned __int64& rhs, __int64& result ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); + // if we subtract, and it gets larger, there's a problem + // Perform test as unsigned to prevent unwanted optimizations + unsigned __int64 tmp = (unsigned __int64)lhs - rhs; + + if( (__int64)tmp <= lhs ) + { + result = (__int64)tmp; + return true; + } + return false; + } + + template < typename E > + static void SubtractThrow( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); + // if we subtract, and it gets larger, there's a problem + // Perform test as unsigned to prevent unwanted optimizations + unsigned __int64 tmp = (unsigned __int64)lhs - rhs; + + if( (__int64)tmp <= lhs ) + { + result = (__int64)tmp; + return; + } + + E::SafeIntOnOverflow(); + } + +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Uint642 > +{ +public: + // If lhs is negative, immediate problem - return must be positive, and subtracting only makes it + // get smaller. If rhs > lhs, then it would also go negative, which is the other case + static bool Subtract( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits< T >::isUint64 && IntTraits< U >::isInt64 ); + if( lhs >= 0 && (unsigned __int64)lhs >= rhs ) + { + result = (unsigned __int64)lhs - rhs; + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits< T >::isUint64 && IntTraits< U >::isInt64 ); + if( lhs >= 0 && (unsigned __int64)lhs >= rhs ) + { + result = (unsigned __int64)lhs - rhs; + return; + } + + E::SafeIntOnOverflow(); + } + +}; + +enum BinaryState +{ + BinaryState_OK, + BinaryState_Int8, + BinaryState_Int16, + BinaryState_Int32 +}; + +template < typename T, typename U > class BinaryMethod +{ +public: + enum + { + // If both operands are unsigned OR + // return type is smaller than rhs OR + // return type is larger and rhs is unsigned + // Then binary operations won't produce unexpected results + method = ( sizeof( T ) <= sizeof( U ) || + SafeIntCompare< T, U >::isBothUnsigned || + !IntTraits< U >::isSigned ) ? BinaryState_OK : + IntTraits< U >::isInt8 ? BinaryState_Int8 : + IntTraits< U >::isInt16 ? BinaryState_Int16 + : BinaryState_Int32 + }; +}; + +#ifdef SAFEINT_DISABLE_BINARY_ASSERT +#define BinaryAssert(x) +#else +#define BinaryAssert(x) SAFEINT_ASSERT(x) +#endif + +template < typename T, typename U, int method > class BinaryAndHelper; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_OK > +{ +public: + static T And( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs & rhs ); } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int8 > +{ +public: + static T And( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs & rhs ) == ( lhs & (unsigned __int8)rhs ) ); + return (T)( lhs & (unsigned __int8)rhs ); + } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int16 > +{ +public: + static T And( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs & rhs ) == ( lhs & (unsigned __int16)rhs ) ); + return (T)( lhs & (unsigned __int16)rhs ); + } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int32 > +{ +public: + static T And( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs & rhs ) == ( lhs & (unsigned __int32)rhs ) ); + return (T)( lhs & (unsigned __int32)rhs ); + } +}; + +template < typename T, typename U, int method > class BinaryOrHelper; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_OK > +{ +public: + static T Or( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs | rhs ); } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int8 > +{ +public: + static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs | rhs ) == ( lhs | (unsigned __int8)rhs ) ); + return (T)( lhs | (unsigned __int8)rhs ); + } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int16 > +{ +public: + static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs | rhs ) == ( lhs | (unsigned __int16)rhs ) ); + return (T)( lhs | (unsigned __int16)rhs ); + } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int32 > +{ +public: + static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs | rhs ) == ( lhs | (unsigned __int32)rhs ) ); + return (T)( lhs | (unsigned __int32)rhs ); + } +}; + +template class BinaryXorHelper; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_OK > +{ +public: + static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs ^ rhs ); } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int8 > +{ +public: + static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int8)rhs ) ); + return (T)( lhs ^ (unsigned __int8)rhs ); + } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int16 > +{ +public: + static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int16)rhs ) ); + return (T)( lhs ^ (unsigned __int16)rhs ); + } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int32 > +{ +public: + static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int32)rhs ) ); + return (T)( lhs ^ (unsigned __int32)rhs ); + } +}; + +/***************** External functions ****************************************/ + +// External functions that can be used where you only need to check one operation +// non-class helper function so that you can check for a cast's validity +// and handle errors how you like +template < typename T, typename U > +inline bool SafeCast( const T From, U& To ) SAFEINT_NOTHROW +{ + return SafeCastHelper< U, T, GetCastMethod< U, T >::method >::Cast( From, To ); +} + +template < typename T, typename U > +inline bool SafeEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( t, u ); +} + +template < typename T, typename U > +inline bool SafeNotEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( t, u ); +} + +template < typename T, typename U > +inline bool SafeGreaterThan( const T t, const U u ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( t, u ); +} + +template < typename T, typename U > +inline bool SafeGreaterThanEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( u, t ); +} + +template < typename T, typename U > +inline bool SafeLessThan( const T t, const U u ) SAFEINT_NOTHROW +{ + return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( u, t ); +} + +template < typename T, typename U > +inline bool SafeLessThanEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( t, u ); +} + +template < typename T, typename U > +inline bool SafeModulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW +{ + return ( ModulusHelper< T, U, ValidComparison< T, U >::method >::Modulus( t, u, result ) == SafeIntNoError ); +} + +template < typename T, typename U > +inline bool SafeMultiply( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::Multiply( t, u, result ); +} + +template < typename T, typename U > +inline bool SafeDivide( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return ( DivisionHelper< T, U, DivisionMethod< T, U >::method >::Divide( t, u, result ) == SafeIntNoError ); +} + +template < typename T, typename U > +inline bool SafeAdd( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return AdditionHelper< T, U, AdditionMethod< T, U >::method >::Addition( t, u, result ); +} + +template < typename T, typename U > +inline bool SafeSubtract( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::Subtract( t, u, result ); +} + +/***************** end external functions ************************************/ + +// Main SafeInt class +// Assumes exceptions can be thrown +template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeInt +{ +public: + SafeInt() SAFEINT_NOTHROW + { + C_ASSERT( NumericType< T >::isInt ); + m_int = 0; + } + + // Having a constructor for every type of int + // avoids having the compiler evade our checks when doing implicit casts - + // e.g., SafeInt s = 0x7fffffff; + SafeInt( const T& i ) SAFEINT_NOTHROW + { + C_ASSERT( NumericType< T >::isInt ); + //always safe + m_int = i; + } + + // provide explicit boolean converter + SafeInt( bool b ) SAFEINT_NOTHROW + { + C_ASSERT( NumericType< T >::isInt ); + m_int = (T)( b ? 1 : 0 ); + } + + template < typename U > + SafeInt(const SafeInt< U, E >& u) SAFEINT_CPP_THROW + { + C_ASSERT( NumericType< T >::isInt ); + *this = SafeInt< T, E >( (U)u ); + } + + template < typename U > + SafeInt( const U& i ) SAFEINT_CPP_THROW + { + C_ASSERT( NumericType< T >::isInt ); + // SafeCast will throw exceptions if i won't fit in type T + SafeCastHelper< T, U, GetCastMethod< T, U >::method >::template CastThrow< E >( i, m_int ); + } + + // The destructor is intentionally commented out - no destructor + // vs. a do-nothing destructor makes a huge difference in + // inlining characteristics. It wasn't doing anything anyway. + // ~SafeInt(){}; + + + // now start overloading operators + // assignment operator + // constructors exist for all int types and will ensure safety + + template < typename U > + SafeInt< T, E >& operator =( const U& rhs ) SAFEINT_CPP_THROW + { + // use constructor to test size + // constructor is optimized to do minimal checking based + // on whether T can contain U + // note - do not change this + *this = SafeInt< T, E >( rhs ); + return *this; + } + + SafeInt< T, E >& operator =( const T& rhs ) SAFEINT_NOTHROW + { + m_int = rhs; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator =( const SafeInt< U, E >& rhs ) SAFEINT_CPP_THROW + { + SafeCastHelper< T, U, GetCastMethod< T, U >::method >::template CastThrow< E >( rhs.Ref(), m_int ); + return *this; + } + + SafeInt< T, E >& operator =( const SafeInt< T, E >& rhs ) SAFEINT_NOTHROW + { + m_int = rhs.m_int; + return *this; + } + + // Casting operators + + operator bool() const SAFEINT_NOTHROW + { + return !!m_int; + } + + operator char() const SAFEINT_CPP_THROW + { + char val; + SafeCastHelper< char, T, GetCastMethod< char, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator signed char() const SAFEINT_CPP_THROW + { + signed char val; + SafeCastHelper< signed char, T, GetCastMethod< signed char, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator unsigned char() const SAFEINT_CPP_THROW + { + unsigned char val; + SafeCastHelper< unsigned char, T, GetCastMethod< unsigned char, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator __int16() const SAFEINT_CPP_THROW + { + __int16 val; + SafeCastHelper< __int16, T, GetCastMethod< __int16, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator unsigned __int16() const SAFEINT_CPP_THROW + { + unsigned __int16 val; + SafeCastHelper< unsigned __int16, T, GetCastMethod< unsigned __int16, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator __int32() const SAFEINT_CPP_THROW + { + __int32 val; + SafeCastHelper< __int32, T, GetCastMethod< __int32, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator unsigned __int32() const SAFEINT_CPP_THROW + { + unsigned __int32 val; + SafeCastHelper< unsigned __int32, T, GetCastMethod< unsigned __int32, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + // The compiler knows that int == __int32 + // but not that long == __int32 + operator long() const SAFEINT_CPP_THROW + { + long val; + SafeCastHelper< long, T, GetCastMethod< long, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator unsigned long() const SAFEINT_CPP_THROW + { + unsigned long val; + SafeCastHelper< unsigned long, T, GetCastMethod< unsigned long, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator __int64() const SAFEINT_CPP_THROW + { + __int64 val; + SafeCastHelper< __int64, T, GetCastMethod< __int64, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator unsigned __int64() const SAFEINT_CPP_THROW + { + unsigned __int64 val; + SafeCastHelper< unsigned __int64, T, GetCastMethod< unsigned __int64, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + +#if defined SAFEINT_USE_WCHAR_T || defined _NATIVE_WCHAR_T_DEFINED + operator wchar_t() const SAFEINT_CPP_THROW + { + wchar_t val; + SafeCastHelper< wchar_t, T, GetCastMethod< wchar_t, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } +#endif + +#ifdef SIZE_T_CAST_NEEDED + // We also need an explicit cast to size_t, or the compiler will complain + // Apparently, only SOME compilers complain, and cl 14.00.50727.42 isn't one of them + // Leave here in case we decide to backport this to an earlier compiler + operator size_t() const SAFEINT_CPP_THROW + { + size_t val; + SafeCastHelper< size_t, T, GetCastMethod< size_t, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } +#endif + + // Also provide a cast operator for floating point types + operator float() const SAFEINT_CPP_THROW + { + float val; + SafeCastHelper< float, T, GetCastMethod< float, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator double() const SAFEINT_CPP_THROW + { + double val; + SafeCastHelper< double, T, GetCastMethod< double, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + operator long double() const SAFEINT_CPP_THROW + { + long double val; + SafeCastHelper< long double, T, GetCastMethod< long double, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + // If you need a pointer to the data + // this could be dangerous, but allows you to correctly pass + // instances of this class to APIs that take a pointer to an integer + // also see overloaded address-of operator below + T* Ptr() SAFEINT_NOTHROW { return &m_int; } + const T* Ptr() const SAFEINT_NOTHROW { return &m_int; } + const T& Ref() const SAFEINT_NOTHROW { return m_int; } + + // Or if SafeInt< T, E >::Ptr() is inconvenient, use the overload + // operator & + // This allows you to do unsafe things! + // It is meant to allow you to more easily + // pass a SafeInt into things like ReadFile + T* operator &() SAFEINT_NOTHROW { return &m_int; } + const T* operator &() const SAFEINT_NOTHROW { return &m_int; } + + // Unary operators + bool operator !() const SAFEINT_NOTHROW { return (!m_int) ? true : false; } + + // operator + (unary) + // note - normally, the '+' and '-' operators will upcast to a signed int + // for T < 32 bits. This class changes behavior to preserve type + const SafeInt< T, E >& operator +() const SAFEINT_NOTHROW { return *this; } + + //unary - + + SafeInt< T, E > operator -() const SAFEINT_CPP_THROW + { + // Note - unsigned still performs the bitwise manipulation + // will warn at level 2 or higher if the value is 32-bit or larger + return SafeInt(NegationHelper::isSigned>::template NegativeThrow(m_int)); + } + + // prefix increment operator + SafeInt< T, E >& operator ++() SAFEINT_CPP_THROW + { + if( m_int != IntTraits< T >::maxInt ) + { + ++m_int; + return *this; + } + E::SafeIntOnOverflow(); + } + + // prefix decrement operator + SafeInt< T, E >& operator --() SAFEINT_CPP_THROW + { + if( m_int != IntTraits< T >::minInt ) + { + --m_int; + return *this; + } + E::SafeIntOnOverflow(); + } + + // note that postfix operators have inherently worse perf + // characteristics + + // postfix increment operator + SafeInt< T, E > operator ++( int ) SAFEINT_CPP_THROW // dummy arg to comply with spec + { + if( m_int != IntTraits< T >::maxInt ) + { + SafeInt< T, E > tmp( m_int ); + + m_int++; + return tmp; + } + E::SafeIntOnOverflow(); + } + + // postfix decrement operator + SafeInt< T, E > operator --( int ) SAFEINT_CPP_THROW // dummy arg to comply with spec + { + if( m_int != IntTraits< T >::minInt ) + { + SafeInt< T, E > tmp( m_int ); + m_int--; + return tmp; + } + E::SafeIntOnOverflow(); + } + + // One's complement + // Note - this operator will normally change size to an int + // cast in return improves perf and maintains type + SafeInt< T, E > operator ~() const SAFEINT_NOTHROW { return SafeInt< T, E >( (T)~m_int ); } + + // Binary operators + // + // arithmetic binary operators + // % modulus + // * multiplication + // / division + // + addition + // - subtraction + // + // For each of the arithmetic operators, you will need to + // use them as follows: + // + // SafeInt c = 2; + // SafeInt i = 3; + // + // SafeInt i2 = i op (char)c; + // OR + // SafeInt i2 = (int)i op c; + // + // The base problem is that if the lhs and rhs inputs are different SafeInt types + // it is not possible in this implementation to determine what type of SafeInt + // should be returned. You have to let the class know which of the two inputs + // need to be the return type by forcing the other value to the base integer type. + // + // Note - as per feedback from Scott Meyers, I'm exploring how to get around this. + // 3.0 update - I'm still thinking about this. It can be done with template metaprogramming, + // but it is tricky, and there's a perf vs. correctness tradeoff where the right answer + // is situational. + // + // The case of: + // + // SafeInt< T, E > i, j, k; + // i = j op k; + // + // works just fine and no unboxing is needed because the return type is not ambiguous. + + // Modulus + // Modulus has some convenient properties - + // first, the magnitude of the return can never be + // larger than the lhs operand, and it must be the same sign + // as well. It does, however, suffer from the same promotion + // problems as comparisons, division and other operations + template < typename U > + SafeInt< T, E > operator %( U rhs ) const SAFEINT_CPP_THROW + { + T result; + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, rhs, result ); + return SafeInt< T, E >( result ); + } + + SafeInt< T, E > operator %( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T result; + ModulusHelper< T, T, ValidComparison< T, T >::method >::template ModulusThrow< E >( m_int, rhs, result ); + return SafeInt< T, E >( result ); + } + + // Modulus assignment + template < typename U > + SafeInt< T, E >& operator %=( U rhs ) SAFEINT_CPP_THROW + { + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator %=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, (U)rhs, m_int ); + return *this; + } + + // Multiplication + template < typename U > + SafeInt< T, E > operator *( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + SafeInt< T, E > operator *( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + MultiplicationHelper< T, T, MultiplicationMethod< T, T >::method >::template MultiplyThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Multiplication assignment + SafeInt< T, E >& operator *=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + { + MultiplicationHelper< T, T, MultiplicationMethod< T, T >::method >::template MultiplyThrow< E >( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator *=( U rhs ) SAFEINT_CPP_THROW + { + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator *=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs.Ref(), m_int ); + return *this; + } + + // Division + template < typename U > + SafeInt< T, E > operator /( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + SafeInt< T, E > operator /( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + DivisionHelper< T, T, DivisionMethod< T, T >::method >::template DivideThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Division assignment + SafeInt< T, E >& operator /=( SafeInt< T, E > i ) SAFEINT_CPP_THROW + { + DivisionHelper< T, T, DivisionMethod< T, T >::method >::template DivideThrow< E >( m_int, (T)i, m_int ); + return *this; + } + + template < typename U > SafeInt< T, E >& operator /=( U i ) SAFEINT_CPP_THROW + { + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, i, m_int ); + return *this; + } + + template < typename U > SafeInt< T, E >& operator /=( SafeInt< U, E > i ) + { + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, (U)i, m_int ); + return *this; + } + + // For addition and subtraction + + // Addition + SafeInt< T, E > operator +( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + AdditionHelper< T, T, AdditionMethod< T, T >::method >::template AdditionThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + template < typename U > + SafeInt< T, E > operator +( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + //addition assignment + SafeInt< T, E >& operator +=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + { + AdditionHelper< T, T, AdditionMethod< T, T >::method >::template AdditionThrow< E >( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator +=( U rhs ) SAFEINT_CPP_THROW + { + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator +=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, (U)rhs, m_int ); + return *this; + } + + // Subtraction + template < typename U > + SafeInt< T, E > operator -( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + SafeInt< T, E > operator -(SafeInt< T, E > rhs) const SAFEINT_CPP_THROW + { + T ret( 0 ); + SubtractionHelper< T, T, SubtractionMethod< T, T >::method >::template SubtractThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Subtraction assignment + SafeInt< T, E >& operator -=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + { + SubtractionHelper< T, T, SubtractionMethod< T, T >::method >::template SubtractThrow< E >( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator -=( U rhs ) SAFEINT_CPP_THROW + { + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator -=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, (U)rhs, m_int ); + return *this; + } + + // Shift operators + // Note - shift operators ALWAYS return the same type as the lhs + // specific version for SafeInt< T, E > not needed - + // code path is exactly the same as for SafeInt< U, E > as rhs + + // Left shift + // Also, shifting > bitcount is undefined - trap in debug +#ifdef SAFEINT_DISABLE_SHIFT_ASSERT + #define ShiftAssert(x) +#else + #define ShiftAssert(x) SAFEINT_ASSERT(x) +#endif + + template < typename U > + SafeInt< T, E > operator <<( U bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); + ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int << bits ) ); + } + + template < typename U > + SafeInt< T, E > operator <<( SafeInt< U, E > bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)IntTraits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int << (U)bits ) ); + } + + // Left shift assignment + + template < typename U > + SafeInt< T, E >& operator <<=( U bits ) SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); + ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + + m_int <<= bits; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator <<=( SafeInt< U, E > bits ) SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)IntTraits< T >::bitCount ); + + m_int <<= (U)bits; + return *this; + } + + // Right shift + template < typename U > + SafeInt< T, E > operator >>( U bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); + ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int >> bits ) ); + } + + template < typename U > + SafeInt< T, E > operator >>( SafeInt< U, E > bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); + ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + + return SafeInt< T, E >( (T)(m_int >> (U)bits) ); + } + + // Right shift assignment + template < typename U > + SafeInt< T, E >& operator >>=( U bits ) SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); + ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + + m_int >>= bits; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator >>=( SafeInt< U, E > bits ) SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)IntTraits< T >::bitCount ); + + m_int >>= (U)bits; + return *this; + } + + // Bitwise operators + // This only makes sense if we're dealing with the same type and size + // demand a type T, or something that fits into a type T + + // Bitwise & + SafeInt< T, E > operator &( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( m_int & (T)rhs ); + } + + template < typename U > + SafeInt< T, E > operator &( U rhs ) const SAFEINT_NOTHROW + { + // we want to avoid setting bits by surprise + // consider the case of lhs = int, value = 0xffffffff + // rhs = char, value = 0xff + // + // programmer intent is to get only the lower 8 bits + // normal behavior is to upcast both sides to an int + // which then sign extends rhs, setting all the bits + + // If you land in the assert, this is because the bitwise operator + // was causing unexpected behavior. Fix is to properly cast your inputs + // so that it works like you meant, not unexpectedly + + return SafeInt< T, E >( BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, rhs ) ); + } + + // Bitwise & assignment + SafeInt< T, E >& operator &=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + { + m_int &= (T)rhs; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator &=( U rhs ) SAFEINT_NOTHROW + { + m_int = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, rhs ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator &=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + { + m_int = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, (U)rhs ); + return *this; + } + + // XOR + SafeInt< T, E > operator ^( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( (T)( m_int ^ (T)rhs ) ); + } + + template < typename U > + SafeInt< T, E > operator ^( U rhs ) const SAFEINT_NOTHROW + { + // If you land in the assert, this is because the bitwise operator + // was causing unexpected behavior. Fix is to properly cast your inputs + // so that it works like you meant, not unexpectedly + + return SafeInt< T, E >( BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, rhs ) ); + } + + // XOR assignment + SafeInt< T, E >& operator ^=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + { + m_int ^= (T)rhs; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator ^=( U rhs ) SAFEINT_NOTHROW + { + m_int = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, rhs ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator ^=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + { + m_int = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, (U)rhs ); + return *this; + } + + // bitwise OR + SafeInt< T, E > operator |( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( (T)( m_int | (T)rhs ) ); + } + + template < typename U > + SafeInt< T, E > operator |( U rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, rhs ) ); + } + + // bitwise OR assignment + SafeInt< T, E >& operator |=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + { + m_int |= (T)rhs; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator |=( U rhs ) SAFEINT_NOTHROW + { + m_int = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, rhs ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator |=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + { + m_int = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, (U)rhs ); + return *this; + } + + // Miscellaneous helper functions + SafeInt< T, E > Min( SafeInt< T, E > test, const T floor = IntTraits< T >::minInt ) const SAFEINT_NOTHROW + { + T tmp = test < m_int ? (T)test : m_int; + return tmp < floor ? floor : tmp; + } + + SafeInt< T, E > Max( SafeInt< T, E > test, const T upper = IntTraits< T >::maxInt ) const SAFEINT_NOTHROW + { + T tmp = test > m_int ? (T)test : m_int; + return tmp > upper ? upper : tmp; + } + + void Swap( SafeInt< T, E >& with ) SAFEINT_NOTHROW + { + T temp( m_int ); + m_int = with.m_int; + with.m_int = temp; + } + + static SafeInt< T, E > SafeAtoI( const char* input ) SAFEINT_CPP_THROW + { + return SafeTtoI( input ); + } + + static SafeInt< T, E > SafeWtoI( const wchar_t* input ) + { + return SafeTtoI( input ); + } + + enum alignBits + { + align2 = 1, + align4 = 2, + align8 = 3, + align16 = 4, + align32 = 5, + align64 = 6, + align128 = 7, + align256 = 8 + }; + + template < alignBits bits > + const SafeInt< T, E >& Align() SAFEINT_CPP_THROW + { + // Zero is always aligned + if( m_int == 0 ) + return *this; + + // We don't support aligning negative numbers at this time + // Can't align unsigned numbers on bitCount (e.g., 8 bits = 256, unsigned char max = 255) + // or signed numbers on bitCount-1 (e.g., 7 bits = 128, signed char max = 127). + // Also makes no sense to try to align on negative or no bits. + + ShiftAssert( ( ( IntTraits::isSigned && bits < (int)IntTraits< T >::bitCount - 1 ) + || ( !IntTraits::isSigned && bits < (int)IntTraits< T >::bitCount ) ) && + bits >= 0 && ( !IntTraits::isSigned || m_int > 0 ) ); + + const T AlignValue = ( (T)1 << bits ) - 1; + + m_int = (T)( ( m_int + AlignValue ) & ~AlignValue ); + + if( m_int <= 0 ) + E::SafeIntOnOverflow(); + + return *this; + } + + // Commonly needed alignments: + const SafeInt< T, E >& Align2() { return Align< align2 >(); } + const SafeInt< T, E >& Align4() { return Align< align4 >(); } + const SafeInt< T, E >& Align8() { return Align< align8 >(); } + const SafeInt< T, E >& Align16() { return Align< align16 >(); } + const SafeInt< T, E >& Align32() { return Align< align32 >(); } + const SafeInt< T, E >& Align64() { return Align< align64 >(); } +private: + + // This is almost certainly not the best optimized version of atoi, + // but it does not display a typical bug where it isn't possible to set MinInt + // and it won't allow you to overflow your integer. + // This is here because it is useful, and it is an example of what + // can be done easily with SafeInt. + template < typename U > + static SafeInt< T, E > SafeTtoI( U* input ) SAFEINT_CPP_THROW + { + U* tmp = input; + SafeInt< T, E > s; + bool negative = false; + + // Bad input, or empty string + if( input == nullptr || input[0] == 0 ) + E::SafeIntOnOverflow(); + + switch( *tmp ) + { + case '-': + tmp++; + negative = true; + break; + case '+': + tmp++; + break; + } + + while( *tmp != 0 ) + { + if( *tmp < '0' || *tmp > '9' ) + break; + + if( (T)s != 0 ) + s *= (T)10; + + if( !negative ) + s += (T)( *tmp - '0' ); + else + s -= (T)( *tmp - '0' ); + + tmp++; + } + + return s; + } + + T m_int; +}; + +// Helper function used to subtract pointers. +// Used to squelch warnings +template +SafeInt SafePtrDiff(const P* p1, const P* p2) SAFEINT_CPP_THROW +{ + return SafeInt( p1 - p2 ); +} + +// Comparison operators + +//Less than +template < typename T, typename U, typename E > +bool operator <( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +bool operator <( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( rhs, (T)lhs ); +} + +template < typename T, typename U, typename E > +bool operator <( SafeInt< U, E > lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, (U)lhs ); +} + +// Greater than +template < typename T, typename U, typename E > +bool operator >( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( lhs, (T)rhs ); +} + +template < typename T, typename U, typename E > +bool operator >( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +bool operator >( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, (U)rhs ); +} + +// Greater than or equal +template < typename T, typename U, typename E > +bool operator >=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +bool operator >=( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( rhs, (T)lhs ); +} + +template < typename T, typename U, typename E > +bool operator >=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( (U)rhs, (T)lhs ); +} + +// Less than or equal +template < typename T, typename U, typename E > +bool operator <=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( lhs, (T)rhs ); +} + +template < typename T, typename U, typename E > +bool operator <=( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +bool operator <=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, (U)rhs ); +} + +// equality +// explicit overload for bool +template < typename T, typename E > +bool operator ==( bool lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return lhs == ( (T)rhs == 0 ? false : true ); +} + +template < typename T, typename E > +bool operator ==( SafeInt< T, E > lhs, bool rhs ) SAFEINT_NOTHROW +{ + return rhs == ( (T)lhs == 0 ? false : true ); +} + +template < typename T, typename U, typename E > +bool operator ==( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals((T)rhs, lhs); +} + +template < typename T, typename U, typename E > +bool operator ==( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +bool operator ==( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, (U)rhs ); +} + +//not equals +template < typename T, typename U, typename E > +bool operator !=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +bool operator !=( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +bool operator !=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( lhs, rhs ); +} + + +template < typename T, typename E > +bool operator !=( bool lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return ( (T)rhs == 0 ? false : true ) != lhs; +} + +template < typename T, typename E > +bool operator !=( SafeInt< T, E > lhs, bool rhs ) SAFEINT_NOTHROW +{ + return ( (T)lhs == 0 ? false : true ) != rhs; +} + + +template < typename T, typename U, typename E, int method > class ModulusSimpleCaseHelper; + +template < typename T, typename E, int method > class ModulusSignedCaseHelper; + +template < typename T, typename E > class ModulusSignedCaseHelper < T, E, true > +{ +public: + static bool SignedCase( SafeInt< T, E > rhs, SafeInt< T, E >& result ) SAFEINT_NOTHROW + { + if( (T)rhs == (T)-1 ) + { + result = 0; + return true; + } + return false; + } +}; + +template < typename T, typename E > class ModulusSignedCaseHelper < T, E, false > +{ +public: + static bool SignedCase( SafeInt< T, E > /*rhs*/, SafeInt< T, E >& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +template < typename T, typename U, typename E > +class ModulusSimpleCaseHelper < T, U, E, true > +{ +public: + static bool ModulusSimpleCase( U lhs, SafeInt< T, E > rhs, SafeInt< T, E >& result ) SAFEINT_CPP_THROW + { + if( rhs != 0 ) + { + if( ModulusSignedCaseHelper< T, E, IntTraits< T >::isSigned >::SignedCase( rhs, result ) ) + return true; + + result = SafeInt< T, E >( (T)( lhs % (T)rhs ) ); + return true; + } + + E::SafeIntOnDivZero(); + } +}; + +template< typename T, typename U, typename E > +class ModulusSimpleCaseHelper < T, U, E, false > +{ +public: + static bool ModulusSimpleCase( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt< T, E >& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +// Modulus +template < typename T, typename U, typename E > +SafeInt< T, E > operator %( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + // Value of return depends on sign of lhs + // This one may not be safe - bounds check in constructor + // if lhs is negative and rhs is unsigned, this will throw an exception. + + // Fast-track the simple case + // same size and same sign + SafeInt< T, E > result; + + if( ModulusSimpleCaseHelper< T, U, E, + sizeof(T) == sizeof(U) && (bool)IntTraits< T >::isSigned == (bool)IntTraits< U >::isSigned >::ModulusSimpleCase( lhs, rhs, result ) ) + return result; + + return SafeInt< T, E >( ( SafeInt< U, E >( lhs ) % (T)rhs ) ); +} + +// Multiplication +template < typename T, typename U, typename E > +SafeInt< T, E > operator *( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( (T)rhs, lhs, ret ); + return SafeInt< T, E >(ret); +} + +template < typename T, typename U, typename E, int method > class DivisionNegativeCornerCaseHelper; + +template < typename T, typename U, typename E > class DivisionNegativeCornerCaseHelper< T, U, E, true > +{ +public: + static bool NegativeCornerCase( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + { + // Problem case - normal casting behavior changes meaning + // flip rhs to positive + // any operator casts now do the right thing + U tmp; + + if( CompileConst< sizeof(T) == 4 >::Value() ) + tmp = lhs/(U)( ~(unsigned __int32)(T)rhs + 1 ); + else + tmp = lhs/(U)( ~(unsigned __int64)(T)rhs + 1 ); + + if( tmp <= (U)IntTraits< T >::maxInt ) + { + result = SafeInt< T, E >( (T)(~(unsigned __int64)tmp + 1) ); + return true; + } + + // Corner case + T maxT = IntTraits< T >::maxInt; + if( tmp == (U)maxT + 1 ) + { + T minT = IntTraits< T >::minInt; + result = SafeInt< T, E >( minT ); + return true; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U, typename E > class DivisionNegativeCornerCaseHelper< T, U, E, false > +{ +public: + static bool NegativeCornerCase( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +template < typename T, typename U, typename E, int method > class DivisionCornerCaseHelper; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper < T, U, E, true > +{ +public: + static bool DivisionCornerCase1( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + { + if( (T)rhs > 0 ) + { + result = SafeInt< T, E >( lhs/(T)rhs ); + return true; + } + + // Now rhs is either negative, or zero + if( (T)rhs != 0 ) + { + if( DivisionNegativeCornerCaseHelper< T, U, E, sizeof( U ) >= 4 && sizeof( T ) <= sizeof( U ) >::NegativeCornerCase( lhs, rhs, result ) ) + return true; + + result = SafeInt< T, E >(lhs/(T)rhs); + return true; + } + + E::SafeIntOnDivZero(); + } +}; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper < T, U, E, false > +{ +public: + static bool DivisionCornerCase1( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +template < typename T, typename U, typename E, int method > class DivisionCornerCaseHelper2; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 < T, U, E, true > +{ +public: + static bool DivisionCornerCase2( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + { + if( lhs == IntTraits< U >::minInt && (T)rhs == -1 ) + { + // corner case of a corner case - lhs = min int, rhs = -1, + // but rhs is the return type, so in essence, we can return -lhs + // if rhs is a larger type than lhs + // If types are wrong, throws + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning(push) +//cast truncates constant value +#pragma warning(disable:4310) +#endif + + if( CompileConst::Value() ) + result = SafeInt< T, E >( (T)( -(T)IntTraits< U >::minInt ) ); + else + E::SafeIntOnOverflow(); + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning(pop) +#endif + + return true; + } + + return false; + } +}; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 < T, U, E, false > +{ +public: + static bool DivisionCornerCase2( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +// Division +template < typename T, typename U, typename E > SafeInt< T, E > operator /( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + // Corner case - has to be handled seperately + SafeInt< T, E > result; + if( DivisionCornerCaseHelper< T, U, E, (int)DivisionMethod< U, T >::method == (int)DivisionState_UnsignedSigned >::DivisionCornerCase1( lhs, rhs, result ) ) + return result; + + if( DivisionCornerCaseHelper2< T, U, E, SafeIntCompare< T, U >::isBothSigned >::DivisionCornerCase2( lhs, rhs, result ) ) + return result; + + // Otherwise normal logic works with addition of bounds check when casting from U->T + U ret; + DivisionHelper< U, T, DivisionMethod< U, T >::method >::template DivideThrow< E >( lhs, (T)rhs, ret ); + return SafeInt< T, E >( ret ); +} + +// Addition +template < typename T, typename U, typename E > +SafeInt< T, E > operator +( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( (T)rhs, lhs, ret ); + return SafeInt< T, E >( ret ); +} + +// Subtraction +template < typename T, typename U, typename E > +SafeInt< T, E > operator -( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + SubtractionHelper< U, T, SubtractionMethod2< U, T >::method >::template SubtractThrow< E >( lhs, rhs.Ref(), ret ); + + return SafeInt< T, E >( ret ); +} + +// Overrides designed to deal with cases where a SafeInt is assigned out +// to a normal int - this at least makes the last operation safe +// += +template < typename T, typename U, typename E > +T& operator +=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator -=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator *=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator /=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator %=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator &=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +T& operator ^=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +T& operator |=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +T& operator <<=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = (T)( SafeInt< T, E >( lhs ) << (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +T& operator >>=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = (T)( SafeInt< T, E >( lhs ) >> (U)rhs ); + return lhs; +} + +// Specific pointer overrides +// Note - this function makes no attempt to ensure +// that the resulting pointer is still in the buffer, only +// that no int overflows happened on the way to getting the new pointer +template < typename T, typename U, typename E > +T*& operator +=( T*& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + // Cast the pointer to a number so we can do arithmetic + SafeInt< size_t, E > ptr_val = reinterpret_cast< size_t >( lhs ); + // Check first that rhs is valid for the type of ptrdiff_t + // and that multiplying by sizeof( T ) doesn't overflow a ptrdiff_t + // Next, we need to add 2 SafeInts of different types, so unbox the ptr_diff + // Finally, cast the number back to a pointer of the correct type + lhs = reinterpret_cast< T* >( (size_t)( ptr_val + (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator -=( T*& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + // Cast the pointer to a number so we can do arithmetic + SafeInt< size_t, E > ptr_val = reinterpret_cast< size_t >( lhs ); + // See above for comments + lhs = reinterpret_cast< T* >( (size_t)( ptr_val - (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator *=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator /=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator %=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator &=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator ^=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator |=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator <<=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator >>=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +// Shift operators +// NOTE - shift operators always return the type of the lhs argument + +// Left shift +template < typename T, typename U, typename E > +SafeInt< U, E > operator <<( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW +{ + ShiftAssert( !IntTraits< T >::isSigned || (T)bits >= 0 ); + ShiftAssert( (T)bits < (int)IntTraits< U >::bitCount ); + + return SafeInt< U, E >( (U)( lhs << (T)bits ) ); +} + +// Right shift +template < typename T, typename U, typename E > +SafeInt< U, E > operator >>( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW +{ + ShiftAssert( !IntTraits< T >::isSigned || (T)bits >= 0 ); + ShiftAssert( (T)bits < (int)IntTraits< U >::bitCount ); + + return SafeInt< U, E >( (U)( lhs >> (T)bits ) ); +} + +// Bitwise operators +// This only makes sense if we're dealing with the same type and size +// demand a type T, or something that fits into a type T. + +// Bitwise & +template < typename T, typename U, typename E > +SafeInt< T, E > operator &( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return SafeInt< T, E >( BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( (T)rhs, lhs ) ); +} + +// Bitwise XOR +template < typename T, typename U, typename E > +SafeInt< T, E > operator ^( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return SafeInt< T, E >(BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( (T)rhs, lhs ) ); +} + +// Bitwise OR +template < typename T, typename U, typename E > +SafeInt< T, E > operator |( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return SafeInt< T, E >( BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( (T)rhs, lhs ) ); +} + +#if SAFEINT_COMPILER == GCC_COMPILER +#pragma GCC diagnostic pop +#endif + +#if SAFEINT_COMPILER == CLANG_COMPILER +#pragma clang diagnostic pop +#endif + +} // utilities +} // safeint3 + diff --git a/src/corehost/cli/json/casablanca/include/cpprest/details/basic_types.h b/src/corehost/cli/json/casablanca/include/cpprest/details/basic_types.h new file mode 100755 index 000000000..ea28f8260 --- /dev/null +++ b/src/corehost/cli/json/casablanca/include/cpprest/details/basic_types.h @@ -0,0 +1,140 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Platform-dependent type definitions +* +* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include +#include +#include +#include +#include "cpprest/details/cpprest_compat.h" + +#ifndef _WIN32 +# define __STDC_LIMIT_MACROS +# include +#else +#include +#endif + +#include "cpprest/details/SafeInt3.hpp" + +namespace utility +{ + +#ifdef _WIN32 +#define _UTF16_STRINGS +#endif + +// We should be using a 64-bit size type for most situations that do +// not involve specifying the size of a memory allocation or buffer. +typedef uint64_t size64_t; + +#ifndef _WIN32 +typedef uint32_t HRESULT; // Needed for PPLX +#endif + +#ifdef _UTF16_STRINGS +// +// On Windows, all strings are wide +// +typedef wchar_t char_t ; +typedef std::wstring string_t; +#define _XPLATSTR(x) L ## x +typedef std::wostringstream ostringstream_t; +typedef std::wofstream ofstream_t; +typedef std::wostream ostream_t; +typedef std::wistream istream_t; +typedef std::wifstream ifstream_t; +typedef std::wistringstream istringstream_t; +typedef std::wstringstream stringstream_t; +#define ucout std::wcout +#define ucin std::wcin +#define ucerr std::wcerr +#else +// +// On POSIX platforms, all strings are narrow +// +typedef char char_t; +typedef std::string string_t; +#define _XPLATSTR(x) x +typedef std::ostringstream ostringstream_t; +typedef std::ofstream ofstream_t; +typedef std::ostream ostream_t; +typedef std::istream istream_t; +typedef std::ifstream ifstream_t; +typedef std::istringstream istringstream_t; +typedef std::stringstream stringstream_t; +#define ucout std::cout +#define ucin std::cin +#define ucerr std::cerr +#endif // endif _UTF16_STRINGS + +#ifndef _TURN_OFF_PLATFORM_STRING +#define U(x) _XPLATSTR(x) +#endif // !_TURN_OFF_PLATFORM_STRING + +}// namespace utility + +typedef char utf8char; +typedef std::string utf8string; +typedef std::stringstream utf8stringstream; +typedef std::ostringstream utf8ostringstream; +typedef std::ostream utf8ostream; +typedef std::istream utf8istream; +typedef std::istringstream utf8istringstream; + +#ifdef _UTF16_STRINGS +typedef wchar_t utf16char; +typedef std::wstring utf16string; +typedef std::wstringstream utf16stringstream; +typedef std::wostringstream utf16ostringstream; +typedef std::wostream utf16ostream; +typedef std::wistream utf16istream; +typedef std::wistringstream utf16istringstream; +#else +typedef char16_t utf16char; +typedef std::u16string utf16string; +typedef std::basic_stringstream utf16stringstream; +typedef std::basic_ostringstream utf16ostringstream; +typedef std::basic_ostream utf16ostream; +typedef std::basic_istream utf16istream; +typedef std::basic_istringstream utf16istringstream; +#endif + + +#if defined(_WIN32) +// Include on everything except Windows Desktop ARM, unless explicitly excluded. +#if !defined(CPPREST_EXCLUDE_WEBSOCKETS) +#if defined(WINAPI_FAMILY) +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && defined(_M_ARM) +#define CPPREST_EXCLUDE_WEBSOCKETS +#endif +#else +#if defined(_M_ARM) +#define CPPREST_EXCLUDE_WEBSOCKETS +#endif +#endif +#endif +#endif diff --git a/src/corehost/cli/json/casablanca/include/cpprest/details/cpprest_compat.h b/src/corehost/cli/json/casablanca/include/cpprest/details/cpprest_compat.h new file mode 100755 index 000000000..240a33899 --- /dev/null +++ b/src/corehost/cli/json/casablanca/include/cpprest/details/cpprest_compat.h @@ -0,0 +1,97 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Standard macros and definitions. +* This header has minimal dependency on windows headers and is safe for use in the public API +* +* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#if defined(_WIN32) // Settings specific to Windows + +#if _MSC_VER >= 1900 +#define CPPREST_NOEXCEPT noexcept +#else +#define CPPREST_NOEXCEPT +#endif + +#define CASABLANCA_UNREFERENCED_PARAMETER(x) (x) + +#include + +#else // End settings specific to Windows + +// Settings common to all but Windows + +#define __declspec(x) __attribute__ ((x)) +#define dllimport +#define novtable /* no novtable equivalent */ +#define __assume(x) do { if (!(x)) __builtin_unreachable(); } while (false) +#define CASABLANCA_UNREFERENCED_PARAMETER(x) (void)x +#define CPPREST_NOEXCEPT noexcept + +#include +#define _ASSERTE(x) assert(x) + +// No SAL on non Windows platforms +#include "cpprest/details/nosal.h" + +#if not defined __cdecl +#if defined cdecl +#define __cdecl __attribute__ ((cdecl)) +#else +#define __cdecl +#endif + +#if defined(__ANDROID__) +// This is needed to disable the use of __thread inside the boost library. +// Android does not support thread local storage -- if boost is included +// without this macro defined, it will create references to __tls_get_addr +// which (while able to link) will not be available at runtime and prevent +// the .so from loading. +#define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION +#endif + +#ifdef __clang__ +#include +#endif + +#endif // defined(__APPLE__) + +#endif + + +#ifdef _NO_ASYNCRTIMP +#define _ASYNCRTIMP +#else +#ifdef _ASYNCRT_EXPORT +#define _ASYNCRTIMP __declspec(dllexport) +#else +#define _ASYNCRTIMP __declspec(dllimport) +#endif +#endif + +#ifdef CASABLANCA_DEPRECATION_NO_WARNINGS +#define CASABLANCA_DEPRECATED(x) +#else +#define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x)) +#endif diff --git a/src/corehost/cli/json/casablanca/include/cpprest/details/nosal.h b/src/corehost/cli/json/casablanca/include/cpprest/details/nosal.h new file mode 100755 index 000000000..4d7e583a7 --- /dev/null +++ b/src/corehost/cli/json/casablanca/include/cpprest/details/nosal.h @@ -0,0 +1,89 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* +* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +***/ + +#pragma once +// selected MS SAL annotations + +#ifdef _In_ +#undef _In_ +#endif +#define _In_ + +#ifdef _Inout_ +#undef _Inout_ +#endif +#define _Inout_ + +#ifdef _Out_ +#undef _Out_ +#endif +#define _Out_ + +#ifdef _In_z_ +#undef _In_z_ +#endif +#define _In_z_ + +#ifdef _Out_z_ +#undef _Out_z_ +#endif +#define _Out_z_ + +#ifdef _Inout_z_ +#undef _Inout_z_ +#endif +#define _Inout_z_ + +#ifdef _In_opt_ +#undef _In_opt_ +#endif +#define _In_opt_ + +#ifdef _Out_opt_ +#undef _Out_opt_ +#endif +#define _Out_opt_ + +#ifdef _Inout_opt_ +#undef _Inout_opt_ +#endif +#define _Inout_opt_ + +#ifdef _Out_writes_ +#undef _Out_writes_ +#endif +#define _Out_writes_(x) + +#ifdef _Out_writes_opt_ +#undef _Out_writes_opt_ +#endif +#define _Out_writes_opt_(x) + +#ifdef _In_reads_ +#undef _In_reads_ +#endif +#define _In_reads_(x) + +#ifdef _Inout_updates_bytes_ +#undef _Inout_updates_bytes_ +#endif +#define _Inout_updates_bytes_(x) \ No newline at end of file diff --git a/src/corehost/cli/json/casablanca/include/cpprest/json.h b/src/corehost/cli/json/casablanca/include/cpprest/json.h new file mode 100755 index 000000000..704cbfad9 --- /dev/null +++ b/src/corehost/cli/json/casablanca/include/cpprest/json.h @@ -0,0 +1,1936 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: JSON parser and writer +* +* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifndef _CASA_JSON_H +#define _CASA_JSON_H + +#include +#include +#include +#include +#include +#include +#include "cpprest/details/basic_types.h" +#include "cpprest/asyncrt_utils.h" + +namespace web +{ +/// Library for parsing and serializing JSON values to and from C++ types. +namespace json +{ + // Various forward declarations. + namespace details + { + class _Value; + class _Number; + class _Null; + class _Boolean; + class _String; + class _Object; + class _Array; + template class JSON_Parser; + } + + namespace details + { + extern bool g_keep_json_object_unsorted; + } + + /// + /// Preserve the order of the name/value pairs when parsing a JSON object. + /// The default is false, which can yield better performance. + /// + /// true if ordering should be preserved when parsing, false otherwise. + /// Note this is a global setting and affects all JSON parsing done. + void _ASYNCRTIMP __cdecl keep_object_element_order(bool keep_order); + +#ifdef _WIN32 +#ifdef _DEBUG +#define ENABLE_JSON_VALUE_VISUALIZER +#endif +#endif + + class number; + class array; + class object; + + /// + /// A JSON value represented as a C++ class. + /// + class value + { + public: + /// + /// This enumeration represents the various kinds of JSON values. + /// + enum value_type + { + /// Number value + Number, + /// Boolean value + Boolean, + /// String value + String, + /// Object value + Object, + /// Array value + Array, + /// Null value + Null + }; + + /// + /// Constructor creating a null value + /// + _ASYNCRTIMP value(); + + /// + /// Constructor creating a JSON number value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP value(int32_t value); + + /// + /// Constructor creating a JSON number value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP value(uint32_t value); + + /// + /// Constructor creating a JSON number value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP value(int64_t value); + + /// + /// Constructor creating a JSON number value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP value(uint64_t value); + + /// + /// Constructor creating a JSON number value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP value(double value); + + /// + /// Constructor creating a JSON Boolean value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP explicit value(bool value); + + /// + /// Constructor creating a JSON string value + /// + /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character width + /// + /// This constructor has O(n) performance because it tries to determine if + /// specified string has characters that should be properly escaped in JSON. + /// + _ASYNCRTIMP explicit value(utility::string_t value); + + /// + /// Constructor creating a JSON string value specifying if the string contains characters to escape + /// + /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character width + /// Whether contains characters + /// that should be escaped in JSON value + /// + /// This constructor has O(1) performance. + /// + _ASYNCRTIMP explicit value(utility::string_t value, bool has_escape_chars); + + /// + /// Constructor creating a JSON string value + /// + /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character width + /// + /// + /// This constructor has O(n) performance because it tries to determine if + /// specified string has characters that should be properly escaped in JSON. + /// + /// + /// This constructor exists in order to avoid string literals matching another constructor, + /// as is very likely. For example, conversion to bool does not require a user-defined conversion, + /// and will therefore match first, which means that the JSON value turns up as a boolean. + /// + /// + _ASYNCRTIMP explicit value(const utility::char_t* value); + + /// + /// Constructor creating a JSON string value + /// + /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character width + /// Whether contains characters + /// + /// + /// This overload has O(1) performance. + /// + /// + /// This constructor exists in order to avoid string literals matching another constructor, + /// as is very likely. For example, conversion to bool does not require a user-defined conversion, + /// and will therefore match first, which means that the JSON value turns up as a boolean. + /// + /// + _ASYNCRTIMP explicit value(const utility::char_t* value, bool has_escape_chars); + + /// + /// Copy constructor + /// + _ASYNCRTIMP value(const value &); + + /// + /// Move constructor + /// + _ASYNCRTIMP value(value &&) CPPREST_NOEXCEPT ; + + /// + /// Assignment operator. + /// + /// The JSON value object that contains the result of the assignment. + _ASYNCRTIMP value &operator=(const value &); + + /// + /// Move assignment operator. + /// + /// The JSON value object that contains the result of the assignment. + _ASYNCRTIMP value &operator=(value &&) CPPREST_NOEXCEPT ; + + // Static factories + + /// + /// Creates a null value + /// + /// A JSON null value + static _ASYNCRTIMP value __cdecl null(); + + /// + /// Creates a number value + /// + /// The C++ value to create a JSON value from + /// A JSON number value + static _ASYNCRTIMP value __cdecl number(double value); + + /// + /// Creates a number value + /// + /// The C++ value to create a JSON value from + /// A JSON number value + static _ASYNCRTIMP value __cdecl number(int32_t value); + + /// + /// Creates a number value + /// + /// The C++ value to create a JSON value from + /// A JSON number value + static _ASYNCRTIMP value __cdecl number(uint32_t value); + + /// + /// Creates a number value + /// + /// The C++ value to create a JSON value from + /// A JSON number value + static _ASYNCRTIMP value __cdecl number(int64_t value); + + /// + /// Creates a number value + /// + /// The C++ value to create a JSON value from + /// A JSON number value + static _ASYNCRTIMP value __cdecl number(uint64_t value); + + /// + /// Creates a Boolean value + /// + /// The C++ value to create a JSON value from + /// A JSON Boolean value + static _ASYNCRTIMP value __cdecl boolean(bool value); + + /// + /// Creates a string value + /// + /// The C++ value to create a JSON value from + /// A JSON string value + /// + /// This overload has O(n) performance because it tries to determine if + /// specified string has characters that should be properly escaped in JSON. + /// + static _ASYNCRTIMP value __cdecl string(utility::string_t value); + + /// + /// Creates a string value specifying if the string contains characters to escape + /// + /// The C++ value to create a JSON value from + /// Whether contains characters + /// that should be escaped in JSON value + /// A JSON string value + /// + /// This overload has O(1) performance. + /// + static _ASYNCRTIMP value __cdecl string(utility::string_t value, bool has_escape_chars); + +#ifdef _WIN32 +private: + // Only used internally by JSON parser. + static _ASYNCRTIMP value __cdecl string(const std::string &value); +public: +#endif + + /// + /// Creates an object value + /// + /// Whether to preserve the original order of the fields + /// An empty JSON object value + static _ASYNCRTIMP json::value __cdecl object(bool keep_order = false); + + /// + /// Creates an object value from a collection of field/values + /// + /// Field names associated with JSON values + /// Whether to preserve the original order of the fields + /// A non-empty JSON object value + static _ASYNCRTIMP json::value __cdecl object(std::vector> fields, bool keep_order = false); + + /// + /// Creates an empty JSON array + /// + /// An empty JSON array value + static _ASYNCRTIMP json::value __cdecl array(); + + /// + /// Creates a JSON array + /// + /// The initial number of elements of the JSON value + /// A JSON array value + static _ASYNCRTIMP json::value __cdecl array(size_t size); + + /// + /// Creates a JSON array + /// + /// A vector of JSON values + /// A JSON array value + static _ASYNCRTIMP json::value __cdecl array(std::vector elements); + + /// + /// Accesses the type of JSON value the current value instance is + /// + /// The value's type + _ASYNCRTIMP json::value::value_type type() const; + + /// + /// Is the current value a null value? + /// + /// true if the value is a null value, false otherwise + bool is_null() const { return type() == Null; }; + + /// + /// Is the current value a number value? + /// + /// true if the value is a number value, false otherwise + bool is_number() const { return type() == Number; } + + /// + /// Is the current value represented as an integer number value? + /// + /// + /// Note that if a json value is a number but represented as a double it can still + /// be retrieved as a integer using as_integer(), however the value will be truncated. + /// + /// true if the value is an integer value, false otherwise. + _ASYNCRTIMP bool is_integer() const; + + /// + /// Is the current value represented as an double number value? + /// + /// + /// Note that if a json value is a number but represented as a int it can still + /// be retrieved as a double using as_double(). + /// + /// true if the value is an double value, false otherwise. + _ASYNCRTIMP bool is_double() const; + + /// + /// Is the current value a Boolean value? + /// + /// true if the value is a Boolean value, false otherwise + bool is_boolean() const { return type() == Boolean; } + + /// + /// Is the current value a string value? + /// + /// true if the value is a string value, false otherwise + bool is_string() const { return type() == String; } + + /// + /// Is the current value an array? + /// + /// true if the value is an array, false otherwise + bool is_array() const { return type() == Array; } + + /// + /// Is the current value an object? + /// + /// true if the value is an object, false otherwise + bool is_object() const { return type() == Object; } + + /// + /// Gets the number of children of the value. + /// + /// The number of children. 0 for all non-composites. + size_t size() const; + + /// + /// Parses a string and construct a JSON value. + /// + /// The C++ value to create a JSON value from, a C++ STL double-byte string + _ASYNCRTIMP static value __cdecl parse(const utility::string_t &value); + + /// + /// Attempts to parse a string and construct a JSON value. + /// + /// The C++ value to create a JSON value from, a C++ STL double-byte string + /// If parsing fails, the error code is greater than 0 + /// The parsed object. Returns web::json::value::null if failed + _ASYNCRTIMP static value __cdecl parse(const utility::string_t &value, std::error_code &errorCode); + + /// + /// Serializes the current JSON value to a C++ string. + /// + /// A string representation of the value + _ASYNCRTIMP utility::string_t serialize() const; + + /// + /// Serializes the current JSON value to a C++ string. + /// + /// A string representation of the value + CASABLANCA_DEPRECATED("This API is deprecated and has been renamed to avoid confusion with as_string(), use ::web::json::value::serialize() instead.") + _ASYNCRTIMP utility::string_t to_string() const; + + /// + /// Parses a JSON value from the contents of an input stream using the native platform character width. + /// + /// The stream to read the JSON value from + /// The JSON value object created from the input stream. + _ASYNCRTIMP static value __cdecl parse(utility::istream_t &input); + + /// + /// Parses a JSON value from the contents of an input stream using the native platform character width. + /// + /// The stream to read the JSON value from + /// If parsing fails, the error code is greater than 0 + /// The parsed object. Returns web::json::value::null if failed + _ASYNCRTIMP static value __cdecl parse(utility::istream_t &input, std::error_code &errorCode); + + /// + /// Writes the current JSON value to a stream with the native platform character width. + /// + /// The stream that the JSON string representation should be written to. + _ASYNCRTIMP void serialize(utility::ostream_t &stream) const; + +#ifdef _WIN32 + /// + /// Parses a JSON value from the contents of a single-byte (UTF8) stream. + /// + /// The stream to read the JSON value from + _ASYNCRTIMP static value __cdecl parse(std::istream& stream); + + /// + /// Parses a JSON value from the contents of a single-byte (UTF8) stream. + /// + /// The stream to read the JSON value from + /// If parsing fails, the error code is greater than 0 + /// The parsed object. Returns web::json::value::null if failed + _ASYNCRTIMP static value __cdecl parse(std::istream& stream, std::error_code& error); + + /// + /// Serializes the content of the value into a single-byte (UTF8) stream. + /// + /// The stream that the JSON string representation should be written to. + _ASYNCRTIMP void serialize(std::ostream& stream) const; +#endif + + /// + /// Converts the JSON value to a C++ double, if and only if it is a number value. + /// Throws if the value is not a number + /// + /// A double representation of the value + _ASYNCRTIMP double as_double() const; + + /// + /// Converts the JSON value to a C++ integer, if and only if it is a number value. + /// Throws if the value is not a number + /// + /// An integer representation of the value + _ASYNCRTIMP int as_integer() const; + + /// + /// Converts the JSON value to a number class, if and only if it is a number value. + /// Throws if the value is not a number + /// + /// An instance of number class + _ASYNCRTIMP const json::number& as_number() const; + + /// + /// Converts the JSON value to a C++ bool, if and only if it is a Boolean value. + /// + /// A C++ bool representation of the value + _ASYNCRTIMP bool as_bool() const; + + /// + /// Converts the JSON value to a json array, if and only if it is an array value. + /// + /// The returned json::array should have the same or shorter lifetime as this + /// An array representation of the value + _ASYNCRTIMP json::array& as_array(); + + /// + /// Converts the JSON value to a json array, if and only if it is an array value. + /// + /// The returned json::array should have the same or shorter lifetime as this + /// An array representation of the value + _ASYNCRTIMP const json::array& as_array() const; + + /// + /// Converts the JSON value to a json object, if and only if it is an object value. + /// + /// An object representation of the value + _ASYNCRTIMP json::object& as_object(); + + /// + /// Converts the JSON value to a json object, if and only if it is an object value. + /// + /// An object representation of the value + _ASYNCRTIMP const json::object& as_object() const; + + /// + /// Converts the JSON value to a C++ STL string, if and only if it is a string value. + /// + /// A C++ STL string representation of the value + _ASYNCRTIMP const utility::string_t& as_string() const; + + /// + /// Compares two JSON values for equality. + /// + /// The JSON value to compare with. + /// True iff the values are equal. + _ASYNCRTIMP bool operator==(const value& other) const; + + /// + /// Compares two JSON values for inequality. + /// + /// The JSON value to compare with. + /// True iff the values are unequal. + bool operator!=(const value& other) const + { + return !((*this) == other); + } + + /// + /// Tests for the presence of a field. + /// + /// The name of the field + /// True if the field exists, false otherwise. + bool has_field(const utility::string_t &key) const; + + /// + /// Accesses a field of a JSON object. + /// + /// The name of the field + /// The value kept in the field; null if the field does not exist + CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release, use json::value::at() instead.") + value get(const utility::string_t &key) const; + + /// + /// Erases an element of a JSON array. Throws if index is out of bounds. + /// + /// The index of the element to erase in the JSON array. + _ASYNCRTIMP void erase(size_t index); + + /// + /// Erases an element of a JSON object. Throws if the key doesn't exist. + /// + /// The key of the element to erase in the JSON object. + _ASYNCRTIMP void erase(const utility::string_t &key); + + /// + /// Accesses an element of a JSON array. Throws when index out of bounds. + /// + /// The index of an element in the JSON array. + /// A reference to the value. + _ASYNCRTIMP json::value& at(size_t index); + + /// + /// Accesses an element of a JSON array. Throws when index out of bounds. + /// + /// The index of an element in the JSON array. + /// A reference to the value. + _ASYNCRTIMP const json::value& at(size_t index) const; + + /// + /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. + /// + /// The key of an element in the JSON object. + /// If the key exists, a reference to the value. + _ASYNCRTIMP json::value& at(const utility::string_t& key); + + /// + /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. + /// + /// The key of an element in the JSON object. + /// If the key exists, a reference to the value. + _ASYNCRTIMP const json::value& at(const utility::string_t& key) const; + + /// + /// Accesses a field of a JSON object. + /// + /// The name of the field + /// A reference to the value kept in the field. + _ASYNCRTIMP value & operator [] (const utility::string_t &key); + +#ifdef _WIN32 +private: + // Only used internally by JSON parser + _ASYNCRTIMP value & operator [] (const std::string &key) + { + // JSON object stores its field map as a unordered_map of string_t, so this conversion is hard to avoid + return operator[](utility::conversions::to_string_t(key)); + } +public: +#endif + + /// + /// Accesses an element of a JSON array. + /// + /// The index of an element in the JSON array + /// The value kept at the array index; null if outside the boundaries of the array + CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release, use json::value::at() instead.") + value get(size_t index) const; + + /// + /// Accesses an element of a JSON array. + /// + /// The index of an element in the JSON array. + /// A reference to the value kept in the field. + _ASYNCRTIMP value & operator [] (size_t index); + + private: + friend class web::json::details::_Object; + friend class web::json::details::_Array; + template friend class web::json::details::JSON_Parser; + +#ifdef _WIN32 + /// + /// Writes the current JSON value as a double-byte string to a string instance. + /// + /// The string that the JSON representation should be written to. + _ASYNCRTIMP void format(std::basic_string &string) const; +#endif + /// + /// Serializes the content of the value into a string instance in UTF8 format + /// + /// The string that the JSON representation should be written to + _ASYNCRTIMP void format(std::basic_string& string) const; + +#ifdef ENABLE_JSON_VALUE_VISUALIZER + explicit value(std::unique_ptr v, value_type kind) : m_value(std::move(v)), m_kind(kind) +#else + explicit value(std::unique_ptr v) : m_value(std::move(v)) +#endif + {} + + std::unique_ptr m_value; +#ifdef ENABLE_JSON_VALUE_VISUALIZER + value_type m_kind; +#endif + }; + + /// + /// A single exception type to represent errors in parsing, converting, and accessing + /// elements of JSON values. + /// + class json_exception : public std::exception + { + private: + std::string _message; + public: + json_exception(const utility::char_t * const &message) : _message(utility::conversions::to_utf8string(message)) { } + + // Must be narrow string because it derives from std::exception + const char* what() const CPPREST_NOEXCEPT + { + return _message.c_str(); + } + }; + + namespace details + { + enum json_error + { + left_over_character_in_stream = 1, + malformed_array_literal, + malformed_comment, + malformed_literal, + malformed_object_literal, + malformed_numeric_literal, + malformed_string_literal, + malformed_token, + mismatched_brances, + nesting, + unexpected_token + }; + + class json_error_category_impl : public std::error_category + { + public: + virtual const char* name() const CPPREST_NOEXCEPT override + { + return "json"; + } + + virtual std::string message(int ev) const override + { + switch (ev) + { + case json_error::left_over_character_in_stream: + return "Left-over characters in stream after parsing a JSON value"; + case json_error::malformed_array_literal: + return "Malformed array literal"; + case json_error::malformed_comment: + return "Malformed comment"; + case json_error::malformed_literal: + return "Malformed literal"; + case json_error::malformed_object_literal: + return "Malformed object literal"; + case json_error::malformed_numeric_literal: + return "Malformed numeric literal"; + case json_error::malformed_string_literal: + return "Malformed string literal"; + case json_error::malformed_token: + return "Malformed token"; + case json_error::mismatched_brances: + return "Mismatched braces"; + case json_error::nesting: + return "Nesting too deep"; + case json_error::unexpected_token: + return "Unexpected token"; + default: + return "Unknown json error"; + } + } + }; + + const json_error_category_impl& json_error_category(); + } + + /// + /// A JSON array represented as a C++ class. + /// + class array + { + typedef std::vector storage_type; + + public: + typedef storage_type::iterator iterator; + typedef storage_type::const_iterator const_iterator; + typedef storage_type::reverse_iterator reverse_iterator; + typedef storage_type::const_reverse_iterator const_reverse_iterator; + typedef storage_type::size_type size_type; + + private: + array() : m_elements() { } + array(size_type size) : m_elements(size) { } + array(storage_type elements) : m_elements(std::move(elements)) { } + + public: + /// + /// Gets the beginning iterator element of the array + /// + /// An iterator to the beginning of the JSON array. + iterator begin() + { + return m_elements.begin(); + } + + /// + /// Gets the beginning const iterator element of the array. + /// + /// A const_iterator to the beginning of the JSON array. + const_iterator begin() const + { + return m_elements.cbegin(); + } + + /// + /// Gets the end iterator element of the array + /// + /// An iterator to the end of the JSON array. + iterator end() + { + return m_elements.end(); + } + + /// + /// Gets the end const iterator element of the array. + /// + /// A const_iterator to the end of the JSON array. + const_iterator end() const + { + return m_elements.cend(); + } + + /// + /// Gets the beginning reverse iterator element of the array + /// + /// An reverse_iterator to the beginning of the JSON array. + reverse_iterator rbegin() + { + return m_elements.rbegin(); + } + + /// + /// Gets the beginning const reverse iterator element of the array + /// + /// An const_reverse_iterator to the beginning of the JSON array. + const_reverse_iterator rbegin() const + { + return m_elements.rbegin(); + } + + /// + /// Gets the end reverse iterator element of the array + /// + /// An reverse_iterator to the end of the JSON array. + reverse_iterator rend() + { + return m_elements.rend(); + } + + /// + /// Gets the end const reverse iterator element of the array + /// + /// An const_reverse_iterator to the end of the JSON array. + const_reverse_iterator rend() const + { + return m_elements.crend(); + } + + /// + /// Gets the beginning const iterator element of the array. + /// + /// A const_iterator to the beginning of the JSON array. + const_iterator cbegin() const + { + return m_elements.cbegin(); + } + + /// + /// Gets the end const iterator element of the array. + /// + /// A const_iterator to the end of the JSON array. + const_iterator cend() const + { + return m_elements.cend(); + } + + /// + /// Gets the beginning const reverse iterator element of the array. + /// + /// A const_reverse_iterator to the beginning of the JSON array. + const_reverse_iterator crbegin() const + { + return m_elements.crbegin(); + } + + /// + /// Gets the end const reverse iterator element of the array. + /// + /// A const_reverse_iterator to the end of the JSON array. + const_reverse_iterator crend() const + { + return m_elements.crend(); + } + + /// + /// Deletes an element of the JSON array. + /// + /// A const_iterator to the element to delete. + /// Iterator to the new location of the element following the erased element. + /// GCC doesn't support erase with const_iterator on vector yet. In the future this should be changed. + iterator erase(iterator position) + { + return m_elements.erase(position); + } + + /// + /// Deletes the element at an index of the JSON array. + /// + /// The index of the element to delete. + void erase(size_type index) + { + if (index >= m_elements.size()) + { + throw json_exception(_XPLATSTR("index out of bounds")); + } + m_elements.erase(m_elements.begin() + index); + } + + /// + /// Accesses an element of a JSON array. Throws when index out of bounds. + /// + /// The index of an element in the JSON array. + /// A reference to the value kept in the field. + json::value& at(size_type index) + { + if (index >= m_elements.size()) + throw json_exception(_XPLATSTR("index out of bounds")); + + return m_elements[index]; + } + + /// + /// Accesses an element of a JSON array. Throws when index out of bounds. + /// + /// The index of an element in the JSON array. + /// A reference to the value kept in the field. + const json::value& at(size_type index) const + { + if (index >= m_elements.size()) + throw json_exception(_XPLATSTR("index out of bounds")); + + return m_elements[index]; + } + + /// + /// Accesses an element of a JSON array. + /// + /// The index of an element in the JSON array. + /// A reference to the value kept in the field. + json::value& operator[](size_type index) + { + msl::safeint3::SafeInt nMinSize(index); + nMinSize += 1; + msl::safeint3::SafeInt nlastSize(m_elements.size()); + if (nlastSize < nMinSize) + m_elements.resize(nMinSize); + + return m_elements[index]; + } + + /// + /// Gets the number of elements of the array. + /// + /// The number of elements. + size_type size() const + { + return m_elements.size(); + } + + private: + storage_type m_elements; + + friend class details::_Array; + template friend class json::details::JSON_Parser; + }; + + /// + /// A JSON object represented as a C++ class. + /// + class object + { + typedef std::vector> storage_type; + + public: + typedef storage_type::iterator iterator; + typedef storage_type::const_iterator const_iterator; + typedef storage_type::reverse_iterator reverse_iterator; + typedef storage_type::const_reverse_iterator const_reverse_iterator; + typedef storage_type::size_type size_type; + + private: + object(bool keep_order = false) : m_elements(), m_keep_order(keep_order) { } + object(storage_type elements, bool keep_order = false) : m_elements(std::move(elements)), m_keep_order(keep_order) + { + if (!keep_order) { + sort(m_elements.begin(), m_elements.end(), compare_pairs); + } + } + + public: + /// + /// Gets the beginning iterator element of the object + /// + /// An iterator to the beginning of the JSON object. + iterator begin() + { + return m_elements.begin(); + } + + /// + /// Gets the beginning const iterator element of the object. + /// + /// A const_iterator to the beginning of the JSON object. + const_iterator begin() const + { + return m_elements.cbegin(); + } + + /// + /// Gets the end iterator element of the object + /// + /// An iterator to the end of the JSON object. + iterator end() + { + return m_elements.end(); + } + + /// + /// Gets the end const iterator element of the object. + /// + /// A const_iterator to the end of the JSON object. + const_iterator end() const + { + return m_elements.cend(); + } + + /// + /// Gets the beginning reverse iterator element of the object + /// + /// An reverse_iterator to the beginning of the JSON object. + reverse_iterator rbegin() + { + return m_elements.rbegin(); + } + + /// + /// Gets the beginning const reverse iterator element of the object + /// + /// An const_reverse_iterator to the beginning of the JSON object. + const_reverse_iterator rbegin() const + { + return m_elements.rbegin(); + } + + /// + /// Gets the end reverse iterator element of the object + /// + /// An reverse_iterator to the end of the JSON object. + reverse_iterator rend() + { + return m_elements.rend(); + } + + /// + /// Gets the end const reverse iterator element of the object + /// + /// An const_reverse_iterator to the end of the JSON object. + const_reverse_iterator rend() const + { + return m_elements.crend(); + } + + /// + /// Gets the beginning const iterator element of the object. + /// + /// A const_iterator to the beginning of the JSON object. + const_iterator cbegin() const + { + return m_elements.cbegin(); + } + + /// + /// Gets the end const iterator element of the object. + /// + /// A const_iterator to the end of the JSON object. + const_iterator cend() const + { + return m_elements.cend(); + } + + /// + /// Gets the beginning const reverse iterator element of the object. + /// + /// A const_reverse_iterator to the beginning of the JSON object. + const_reverse_iterator crbegin() const + { + return m_elements.crbegin(); + } + + /// + /// Gets the end const reverse iterator element of the object. + /// + /// A const_reverse_iterator to the end of the JSON object. + const_reverse_iterator crend() const + { + return m_elements.crend(); + } + + /// + /// Deletes an element of the JSON object. + /// + /// A const_iterator to the element to delete. + /// Iterator to the new location of the element following the erased element. + /// GCC doesn't support erase with const_iterator on vector yet. In the future this should be changed. + iterator erase(iterator position) + { + return m_elements.erase(position); + } + + /// + /// Deletes an element of the JSON object. If the key doesn't exist, this method throws. + /// + /// The key of an element in the JSON object. + void erase(const utility::string_t &key) + { + auto iter = find_by_key(key); + if (iter == m_elements.end()) + { + throw web::json::json_exception(_XPLATSTR("Key not found")); + } + + m_elements.erase(iter); + } + + /// + /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. + /// + /// The key of an element in the JSON object. + /// If the key exists, a reference to the value kept in the field. + json::value& at(const utility::string_t& key) + { + auto iter = find_by_key(key); + if (iter == m_elements.end()) + { + throw web::json::json_exception(_XPLATSTR("Key not found")); + } + + return iter->second; + } + + /// + /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. + /// + /// The key of an element in the JSON object. + /// If the key exists, a reference to the value kept in the field. + const json::value& at(const utility::string_t& key) const + { + auto iter = find_by_key(key); + if (iter == m_elements.end()) + { + throw web::json::json_exception(_XPLATSTR("Key not found")); + } + + return iter->second; + } + + /// + /// Accesses an element of a JSON object. + /// + /// The key of an element in the JSON object. + /// If the key exists, a reference to the value kept in the field, otherwise a newly created null value that will be stored for the given key. + json::value& operator[](const utility::string_t& key) + { + auto iter = find_insert_location(key); + + if (iter == m_elements.end() || key != iter->first) + { + return m_elements.insert(iter, std::pair(key, value()))->second; + } + + return iter->second; + } + + /// + /// Gets an iterator to an element of a JSON object. + /// + /// The key of an element in the JSON object. + /// A const iterator to the value kept in the field. + const_iterator find(const utility::string_t& key) const + { + return find_by_key(key); + } + + /// + /// Gets the number of elements of the object. + /// + /// The number of elements. + size_type size() const + { + return m_elements.size(); + } + + /// + /// Checks if there are any elements in the JSON object. + /// + /// True iff empty. + bool empty() const + { + return m_elements.empty(); + } + private: + + static bool compare_pairs(const std::pair& p1, const std::pair& p2) + { + return p1.first < p2.first; + } + static bool compare_with_key(const std::pair& p1, const utility::string_t& key) + { + return p1.first < key; + } + + storage_type::iterator find_insert_location(const utility::string_t &key) + { + if (m_keep_order) + { + return std::find_if(m_elements.begin(), m_elements.end(), + [&key](const std::pair& p) { + return p.first == key; + }); + } + else + { + return std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key); + } + } + + storage_type::const_iterator find_by_key(const utility::string_t& key) const + { + if (m_keep_order) + { + return std::find_if(m_elements.begin(), m_elements.end(), + [&key](const std::pair& p) { + return p.first == key; + }); + } + else + { + auto iter = std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key); + if (iter != m_elements.end() && key != iter->first) + { + return m_elements.end(); + } + return iter; + } + } + + storage_type::iterator find_by_key(const utility::string_t& key) + { + auto iter = find_insert_location(key); + if (iter != m_elements.end() && key != iter->first) + { + return m_elements.end(); + } + return iter; + } + + storage_type m_elements; + bool m_keep_order; + friend class details::_Object; + + template friend class json::details::JSON_Parser; + }; + + /// + /// A JSON number represented as a C++ class. + /// + class number + { + // Note that these constructors make sure that only negative integers are stored as signed int64 (while others convert to unsigned int64). + // This helps handling number objects e.g. comparing two numbers. + + number(double value) : m_value(value), m_type(double_type) { } + number(int32_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) { } + number(uint32_t value) : m_intval(value), m_type(unsigned_type) { } + number(int64_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) { } + number(uint64_t value) : m_uintval(value), m_type(unsigned_type) { } + + public: + + /// + /// Does the number fit into int32? + /// + /// true if the number fits into int32, false otherwise + _ASYNCRTIMP bool is_int32() const; + + /// + /// Does the number fit into unsigned int32? + /// + /// true if the number fits into unsigned int32, false otherwise + _ASYNCRTIMP bool is_uint32() const; + + /// + /// Does the number fit into int64? + /// + /// true if the number fits into int64, false otherwise + _ASYNCRTIMP bool is_int64() const; + + /// + /// Does the number fit into unsigned int64? + /// + /// true if the number fits into unsigned int64, false otherwise + bool is_uint64() const + { + switch (m_type) + { + case signed_type : return m_intval >= 0; + case unsigned_type : return true; + case double_type : + default : + return false; + } + } + + /// + /// Converts the JSON number to a C++ double. + /// + /// A double representation of the number + double to_double() const + { + switch (m_type) + { + case double_type : return m_value; + case signed_type : return static_cast(m_intval); + case unsigned_type : return static_cast(m_uintval); + default : return false; + } + } + + /// + /// Converts the JSON number to int32. + /// + /// An int32 representation of the number + int32_t to_int32() const + { + if (m_type == double_type) + return static_cast(m_value); + else + return static_cast(m_intval); + } + + /// + /// Converts the JSON number to unsigned int32. + /// + /// An usigned int32 representation of the number + uint32_t to_uint32() const + { + if (m_type == double_type) + return static_cast(m_value); + else + return static_cast(m_intval); + } + + /// + /// Converts the JSON number to int64. + /// + /// An int64 representation of the number + int64_t to_int64() const + { + if (m_type == double_type) + return static_cast(m_value); + else + return static_cast(m_intval); + } + + /// + /// Converts the JSON number to unsigned int64. + /// + /// An unsigned int64 representation of the number + uint64_t to_uint64() const + { + if (m_type == double_type) + return static_cast(m_value); + else + return static_cast(m_intval); + } + + /// + /// Is the number represented internally as an integral type? + /// + /// true if the number is represented as an integral type, false otherwise + bool is_integral() const + { + return m_type != double_type; + } + + /// + /// Compares two JSON numbers for equality. + /// + /// The JSON number to compare with. + /// True iff the numbers are equal. + bool operator==(const number &other) const + { + if (m_type != other.m_type) + return false; + + switch (m_type) + { + case json::number::type::signed_type : + return m_intval == other.m_intval; + case json::number::type::unsigned_type : + return m_uintval == other.m_uintval; + case json::number::type::double_type : + return m_value == other.m_value; + } + __assume(0); + } + + private: + union + { + int64_t m_intval; + uint64_t m_uintval; + double m_value; + }; + + enum type + { + signed_type=0, unsigned_type, double_type + } m_type; + + friend class details::_Number; + }; + + namespace details + { + class _Value + { + public: + virtual std::unique_ptr<_Value> _copy_value() = 0; + + virtual bool has_field(const utility::string_t &) const { return false; } + virtual value get_field(const utility::string_t &) const { throw json_exception(_XPLATSTR("not an object")); } + virtual value get_element(array::size_type) const { throw json_exception(_XPLATSTR("not an array")); } + + virtual value &index(const utility::string_t &) { throw json_exception(_XPLATSTR("not an object")); } + virtual value &index(array::size_type) { throw json_exception(_XPLATSTR("not an array")); } + + virtual const value &cnst_index(const utility::string_t &) const { throw json_exception(_XPLATSTR("not an object")); } + virtual const value &cnst_index(array::size_type) const { throw json_exception(_XPLATSTR("not an array")); } + + // Common function used for serialization to strings and streams. + virtual void serialize_impl(std::string& str) const + { + format(str); + } +#ifdef _WIN32 + virtual void serialize_impl(std::wstring& str) const + { + format(str); + } +#endif + + virtual utility::string_t to_string() const + { + utility::string_t str; + serialize_impl(str); + return str; + } + + virtual json::value::value_type type() const { return json::value::Null; } + + virtual bool is_integer() const { throw json_exception(_XPLATSTR("not a number")); } + virtual bool is_double() const { throw json_exception(_XPLATSTR("not a number")); } + + virtual const json::number& as_number() { throw json_exception(_XPLATSTR("not a number")); } + virtual double as_double() const { throw json_exception(_XPLATSTR("not a number")); } + virtual int as_integer() const { throw json_exception(_XPLATSTR("not a number")); } + virtual bool as_bool() const { throw json_exception(_XPLATSTR("not a boolean")); } + virtual json::array& as_array() { throw json_exception(_XPLATSTR("not an array")); } + virtual const json::array& as_array() const { throw json_exception(_XPLATSTR("not an array")); } + virtual json::object& as_object() { throw json_exception(_XPLATSTR("not an object")); } + virtual const json::object& as_object() const { throw json_exception(_XPLATSTR("not an object")); } + virtual const utility::string_t& as_string() const { throw json_exception(_XPLATSTR("not a string")); } + + virtual size_t size() const { return 0; } + + virtual ~_Value() {} + + protected: + _Value() {} + + virtual void format(std::basic_string& stream) const + { + stream.append("null"); + } +#ifdef _WIN32 + virtual void format(std::basic_string& stream) const + { + stream.append(L"null"); + } +#endif + private: + + friend class web::json::value; + }; + + class _Null : public _Value + { + public: + virtual std::unique_ptr<_Value> _copy_value() + { + return utility::details::make_unique<_Null>(); + } + virtual json::value::value_type type() const { return json::value::Null; } + }; + + class _Number : public _Value + { + public: + _Number(double value) : m_number(value) { } + _Number(int32_t value) : m_number(value) { } + _Number(uint32_t value) : m_number(value) { } + _Number(int64_t value) : m_number(value) { } + _Number(uint64_t value) : m_number(value) { } + + virtual std::unique_ptr<_Value> _copy_value() + { + return utility::details::make_unique<_Number>(*this); + } + + virtual json::value::value_type type() const { return json::value::Number; } + + virtual bool is_integer() const { return m_number.is_integral(); } + virtual bool is_double() const { return !m_number.is_integral(); } + + virtual double as_double() const + { + return m_number.to_double(); + } + + virtual int as_integer() const + { + return m_number.to_int32(); + } + + virtual const number& as_number() { return m_number; } + + protected: + virtual void format(std::basic_string& stream) const ; +#ifdef _WIN32 + virtual void format(std::basic_string& stream) const; +#endif + private: + template friend class json::details::JSON_Parser; + + json::number m_number; + }; + + class _Boolean : public _Value + { + public: + _Boolean(bool value) : m_value(value) { } + + virtual std::unique_ptr<_Value> _copy_value() + { + return utility::details::make_unique<_Boolean>(*this); + } + + virtual json::value::value_type type() const { return json::value::Boolean; } + + virtual bool as_bool() const { return m_value; } + + protected: + virtual void format(std::basic_string& stream) const + { + stream.append(m_value ? "true" : "false"); + } + +#ifdef _WIN32 + virtual void format(std::basic_string& stream) const + { + stream.append(m_value ? L"true" : L"false"); + } +#endif + private: + template friend class json::details::JSON_Parser; + bool m_value; + }; + + class _String : public _Value + { + public: + + _String(utility::string_t value) : m_string(std::move(value)) + { + m_has_escape_char = has_escape_chars(*this); + } + _String(utility::string_t value, bool escaped_chars) + : m_string(std::move(value)), + m_has_escape_char(escaped_chars) + { } + +#ifdef _WIN32 + _String(std::string &&value) : m_string(utility::conversions::to_utf16string(std::move(value))) + { + m_has_escape_char = has_escape_chars(*this); + } + _String(std::string &&value, bool escape_chars) + : m_string(utility::conversions::to_utf16string(std::move(value))), + m_has_escape_char(escape_chars) + { } +#endif + + virtual std::unique_ptr<_Value> _copy_value() + { + return utility::details::make_unique<_String>(*this); + } + + virtual json::value::value_type type() const { return json::value::String; } + + virtual const utility::string_t & as_string() const; + + virtual void serialize_impl(std::string& str) const + { + serialize_impl_char_type(str); + } +#ifdef _WIN32 + virtual void serialize_impl(std::wstring& str) const + { + serialize_impl_char_type(str); + } +#endif + + protected: + virtual void format(std::basic_string& str) const; +#ifdef _WIN32 + virtual void format(std::basic_string& str) const; +#endif + + private: + friend class _Object; + friend class _Array; + + size_t get_reserve_size() const + { + return m_string.size() + 2; + } + + template + void serialize_impl_char_type(std::basic_string& str) const + { + // To avoid repeated allocations reserve some space all up front. + // size of string + 2 for quotes + str.reserve(get_reserve_size()); + format(str); + } + + std::string as_utf8_string() const; + utf16string as_utf16_string() const; + + utility::string_t m_string; + + // There are significant performance gains that can be made by knowning whether + // or not a character that requires escaping is present. + bool m_has_escape_char; + static bool has_escape_chars(const _String &str); + }; + + template + _ASYNCRTIMP void append_escape_string(std::basic_string& str, const std::basic_string& escaped); + + void format_string(const utility::string_t& key, utility::string_t& str); + +#ifdef _WIN32 + void format_string(const utility::string_t& key, std::string& str); +#endif + + class _Object : public _Value + { + public: + + _Object(bool keep_order) : m_object(keep_order) { } + _Object(object::storage_type fields, bool keep_order) : m_object(std::move(fields), keep_order) { } + + virtual std::unique_ptr<_Value> _copy_value() + { + return utility::details::make_unique<_Object>(*this); + } + + virtual json::object& as_object() { return m_object; } + + virtual const json::object& as_object() const { return m_object; } + + virtual json::value::value_type type() const { return json::value::Object; } + + virtual bool has_field(const utility::string_t &) const; + + virtual json::value &index(const utility::string_t &key); + + bool is_equal(const _Object* other) const + { + if (m_object.size() != other->m_object.size()) + return false; + + return std::equal(std::begin(m_object), std::end(m_object), std::begin(other->m_object)); + } + + virtual void serialize_impl(std::string& str) const + { + // To avoid repeated allocations reserve some space all up front. + str.reserve(get_reserve_size()); + format(str); + } +#ifdef _WIN32 + virtual void serialize_impl(std::wstring& str) const + { + // To avoid repeated allocations reserve some space all up front. + str.reserve(get_reserve_size()); + format(str); + } +#endif + size_t size() const { return m_object.size(); } + + protected: + virtual void format(std::basic_string& str) const + { + format_impl(str); + } +#ifdef _WIN32 + virtual void format(std::basic_string& str) const + { + format_impl(str); + } +#endif + + private: + json::object m_object; + + template friend class json::details::JSON_Parser; + + template + void format_impl(std::basic_string& str) const + { + str.push_back('{'); + if(!m_object.empty()) + { + auto lastElement = m_object.end() - 1; + for (auto iter = m_object.begin(); iter != lastElement; ++iter) + { + format_string(iter->first, str); + str.push_back(':'); + iter->second.format(str); + str.push_back(','); + } + format_string(lastElement->first, str); + str.push_back(':'); + lastElement->second.format(str); + } + str.push_back('}'); + } + + size_t get_reserve_size() const + { + // This is a heuristic we can tune more in the future: + // Basically size of string plus + // sum size of value if an object, array, or string. + size_t reserveSize = 2; // For brackets {} + for(auto iter = m_object.begin(); iter != m_object.end(); ++iter) + { + reserveSize += iter->first.length() + 2; // 2 for quotes + size_t valueSize = iter->second.size() * 20; // Multipler by each object/array element + if(valueSize == 0) + { + if(iter->second.type() == json::value::String) + { + valueSize = static_cast<_String *>(iter->second.m_value.get())->get_reserve_size(); + } + else + { + valueSize = 5; // true, false, or null + } + } + reserveSize += valueSize; + } + return reserveSize; + } + }; + + class _Array : public _Value + { + public: + _Array() {} + _Array(array::size_type size) : m_array(size) {} + _Array(array::storage_type elements) : m_array(std::move(elements)) { } + + virtual std::unique_ptr<_Value> _copy_value() + { + return utility::details::make_unique<_Array>(*this); + } + + virtual json::value::value_type type() const { return json::value::Array; } + + virtual json::array& as_array() { return m_array; } + virtual const json::array& as_array() const { return m_array; } + + virtual json::value &index(json::array::size_type index) + { + return m_array[index]; + } + + bool is_equal(const _Array* other) const + { + if ( m_array.size() != other->m_array.size()) + return false; + + auto iterT = m_array.cbegin(); + auto iterO = other->m_array.cbegin(); + auto iterTe = m_array.cend(); + auto iterOe = other->m_array.cend(); + + for (; iterT != iterTe && iterO != iterOe; ++iterT, ++iterO) + { + if ( *iterT != *iterO ) + return false; + } + + return true; + } + + virtual void serialize_impl(std::string& str) const + { + // To avoid repeated allocations reserve some space all up front. + str.reserve(get_reserve_size()); + format(str); + } +#ifdef _WIN32 + virtual void serialize_impl(std::wstring& str) const + { + // To avoid repeated allocations reserve some space all up front. + str.reserve(get_reserve_size()); + format(str); + } +#endif + size_t size() const { return m_array.size(); } + + protected: + virtual void format(std::basic_string& str) const + { + format_impl(str); + } +#ifdef _WIN32 + virtual void format(std::basic_string& str) const + { + format_impl(str); + } +#endif + private: + json::array m_array; + + template friend class json::details::JSON_Parser; + + template + void format_impl(std::basic_string& str) const + { + str.push_back('['); + if(!m_array.m_elements.empty()) + { + auto lastElement = m_array.m_elements.end() - 1; + for (auto iter = m_array.m_elements.begin(); iter != lastElement; ++iter) + { + iter->format(str); + str.push_back(','); + } + lastElement->format(str); + } + str.push_back(']'); + } + + size_t get_reserve_size() const + { + // This is a heuristic we can tune more in the future: + // Basically sum size of each value if an object, array, or string by a multiplier. + size_t reserveSize = 2; // For brackets [] + for(auto iter = m_array.cbegin(); iter != m_array.cend(); ++iter) + { + size_t valueSize = iter->size() * 20; // Per each nested array/object + + if(valueSize == 0) + valueSize = 5; // true, false, or null + + reserveSize += valueSize; + } + return reserveSize; + } + }; + } // namespace details + + /// + /// Gets the number of children of the value. + /// + /// The number of children. 0 for all non-composites. + inline size_t json::value::size() const + { + return m_value->size(); + } + + /// + /// Test for the presence of a field. + /// + /// The name of the field + /// True if the field exists, false otherwise. + inline bool json::value::has_field(const utility::string_t& key) const + { + return m_value->has_field(key); + } + + /// + /// Access a field of a JSON object. + /// + /// The name of the field + /// The value kept in the field; null if the field does not exist + inline json::value json::value::get(const utility::string_t& key) const + { + return m_value->get_field(key); + } + + /// + /// Access an element of a JSON array. + /// + /// The index of an element in the JSON array + /// The value kept at the array index; null if outside the boundaries of the array + inline json::value json::value::get(size_t index) const + { + return m_value->get_element(index); + } + + /// + /// A standard std::ostream operator to facilitate writing JSON values to streams. + /// + /// The output stream to write the JSON value to. + /// The JSON value to be written to the stream. + /// The output stream object + _ASYNCRTIMP utility::ostream_t& __cdecl operator << (utility::ostream_t &os, const json::value &val); + + /// + /// A standard std::istream operator to facilitate reading JSON values from streams. + /// + /// The input stream to read the JSON value from. + /// The JSON value object read from the stream. + /// The input stream object. + _ASYNCRTIMP utility::istream_t& __cdecl operator >> (utility::istream_t &is, json::value &val); +}} + +#endif diff --git a/src/corehost/cli/json/casablanca/include/stdafx.h b/src/corehost/cli/json/casablanca/include/stdafx.h new file mode 100644 index 000000000..553d828a6 --- /dev/null +++ b/src/corehost/cli/json/casablanca/include/stdafx.h @@ -0,0 +1,109 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Pre-compiled headers +* +* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-local-typedef" +#endif + +#include +#include +#ifdef _WIN32 +#ifdef CPPREST_TARGET_XP +#include +#ifndef _WIN32_WINNT +#define _WIN32_WINNT _WIN32_WINNT_WS03 //Windows XP with SP2 +#endif +#endif +#include +// use the debug version of the CRT if _DEBUG is defined +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#endif + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#define NOMINMAX +#endif + +#include +#include + +// Windows Header Files: +#if !defined(__cplusplus_winrt) +#include + +#endif // #if !defined(__cplusplus_winrt) +#else // LINUX or APPLE +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include "pthread.h" +#include +#include +#include +#include +#endif // _WIN32 + +// Macro indicating the C++ Rest SDK product itself is being built. +// This is to help track how many developers are directly building from source themselves. +#define _CASA_BUILD_FROM_SRC + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// json +#include "cpprest/json.h" + +#if defined(max) +#error: max macro defined -- make sure to #define NOMINMAX before including windows.h +#endif +#if defined(min) +#error: min macro defined -- make sure to #define NOMINMAX before including windows.h +#endif + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + diff --git a/src/corehost/cli/json/casablanca/src/json/json.cpp b/src/corehost/cli/json/casablanca/src/json/json.cpp new file mode 100644 index 000000000..77380460c --- /dev/null +++ b/src/corehost/cli/json/casablanca/src/json/json.cpp @@ -0,0 +1,495 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: JSON parser and writer +* +* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#include "stdafx.h" + +using namespace web; + +bool json::details::g_keep_json_object_unsorted = false; +void json::keep_object_element_order(bool keep_order) +{ + json::details::g_keep_json_object_unsorted = keep_order; +} + +utility::ostream_t& web::json::operator << (utility::ostream_t &os, const web::json::value &val) +{ + val.serialize(os); + return os; +} + +utility::istream_t& web::json::operator >> (utility::istream_t &is, json::value &val) +{ + val = json::value::parse(is); + return is; +} + +web::json::value::value() : + m_value(utility::details::make_unique()) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,m_kind(value::Null) +#endif + { } + +web::json::value::value(int32_t value) : + m_value(utility::details::make_unique(value)) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,m_kind(value::Number) +#endif + { } + +web::json::value::value(uint32_t value) : + m_value(utility::details::make_unique(value)) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,m_kind(value::Number) +#endif + { } + +web::json::value::value(int64_t value) : + m_value(utility::details::make_unique(value)) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,m_kind(value::Number) +#endif + { } + +web::json::value::value(uint64_t value) : + m_value(utility::details::make_unique(value)) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,m_kind(value::Number) +#endif + { } + +web::json::value::value(double value) : + m_value(utility::details::make_unique(value)) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,m_kind(value::Number) +#endif + { } + +web::json::value::value(bool value) : + m_value(utility::details::make_unique(value)) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,m_kind(value::Boolean) +#endif + { } + +web::json::value::value(utility::string_t value) : + m_value(utility::details::make_unique(std::move(value))) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,m_kind(value::String) +#endif + { } + +web::json::value::value(utility::string_t value, bool has_escape_chars) : +m_value(utility::details::make_unique(std::move(value), has_escape_chars)) +#ifdef ENABLE_JSON_VALUE_VISUALIZER +, m_kind(value::String) +#endif +{ } + +web::json::value::value(const utility::char_t* value) : + m_value(utility::details::make_unique(value)) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,m_kind(value::String) +#endif + { } + +web::json::value::value(const utility::char_t* value, bool has_escape_chars) : +m_value(utility::details::make_unique(utility::string_t(value), has_escape_chars)) +#ifdef ENABLE_JSON_VALUE_VISUALIZER +, m_kind(value::String) +#endif +{ } + +web::json::value::value(const value &other) : + m_value(other.m_value->_copy_value()) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,m_kind(other.m_kind) +#endif + { } + +web::json::value &web::json::value::operator=(const value &other) +{ + if(this != &other) + { + m_value = std::unique_ptr(other.m_value->_copy_value()); +#ifdef ENABLE_JSON_VALUE_VISUALIZER + m_kind = other.m_kind; +#endif + } + return *this; +} + +web::json::value::value(value &&other) CPPREST_NOEXCEPT : + m_value(std::move(other.m_value)) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,m_kind(other.m_kind) +#endif +{} + +web::json::value &web::json::value::operator=(web::json::value &&other) CPPREST_NOEXCEPT +{ + if(this != &other) + { + m_value.swap(other.m_value); +#ifdef ENABLE_JSON_VALUE_VISUALIZER + m_kind = other.m_kind; +#endif + } + return *this; +} + +web::json::value web::json::value::null() +{ + return web::json::value(); +} + +web::json::value web::json::value::number(double value) +{ + return web::json::value(value); +} + +web::json::value web::json::value::number(int32_t value) +{ + return web::json::value(value); +} + +web::json::value web::json::value::number(uint32_t value) +{ + return web::json::value(value); +} + +web::json::value web::json::value::number(int64_t value) +{ + return web::json::value(value); +} + +web::json::value web::json::value::number(uint64_t value) +{ + return web::json::value(value); +} + +web::json::value web::json::value::boolean(bool value) +{ + return web::json::value(value); +} + +web::json::value web::json::value::string(utility::string_t value) +{ + std::unique_ptr ptr = utility::details::make_unique(std::move(value)); + return web::json::value(std::move(ptr) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,value::String +#endif + ); +} + +web::json::value web::json::value::string(utility::string_t value, bool has_escape_chars) +{ + std::unique_ptr ptr = utility::details::make_unique(std::move(value), has_escape_chars); + return web::json::value(std::move(ptr) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,value::String +#endif + ); +} + +#ifdef _WIN32 +web::json::value web::json::value::string(const std::string &value) +{ + std::unique_ptr ptr = utility::details::make_unique(utility::conversions::to_utf16string(value)); + return web::json::value(std::move(ptr) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,value::String +#endif + ); +} +#endif + +web::json::value web::json::value::object(bool keep_order) +{ + std::unique_ptr ptr = utility::details::make_unique(keep_order); + return web::json::value(std::move(ptr) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,value::Object +#endif + ); +} + +web::json::value web::json::value::object(std::vector> fields, bool keep_order) +{ + std::unique_ptr ptr = utility::details::make_unique(std::move(fields), keep_order); + return web::json::value(std::move(ptr) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,value::Object +#endif + ); +} + +web::json::value web::json::value::array() +{ + std::unique_ptr ptr = utility::details::make_unique(); + return web::json::value(std::move(ptr) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,value::Array +#endif + ); +} + +web::json::value web::json::value::array(size_t size) +{ + std::unique_ptr ptr = utility::details::make_unique(size); + return web::json::value(std::move(ptr) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,value::Array +#endif + ); +} + +web::json::value web::json::value::array(std::vector elements) +{ + std::unique_ptr ptr = utility::details::make_unique(std::move(elements)); + return web::json::value(std::move(ptr) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + ,value::Array +#endif + ); +} + +const web::json::number& web::json::value::as_number() const +{ + return m_value->as_number(); +} + +double web::json::value::as_double() const +{ + return m_value->as_double(); +} + +int web::json::value::as_integer() const +{ + return m_value->as_integer(); +} + +bool web::json::value::as_bool() const +{ + return m_value->as_bool(); +} + +json::array& web::json::value::as_array() +{ + return m_value->as_array(); +} + +const json::array& web::json::value::as_array() const +{ + return m_value->as_array(); +} + +json::object& web::json::value::as_object() +{ + return m_value->as_object(); +} + +const json::object& web::json::value::as_object() const +{ + return m_value->as_object(); +} + +bool web::json::number::is_int32() const +{ + switch (m_type) + { + case signed_type : return m_intval >= std::numeric_limits::min() && m_intval <= std::numeric_limits::max(); + case unsigned_type : return m_uintval <= std::numeric_limits::max(); + case double_type : + default : + return false; + } +} + +bool web::json::number::is_uint32() const +{ + switch (m_type) + { + case signed_type : return m_intval >= 0 && m_intval <= std::numeric_limits::max(); + case unsigned_type : return m_uintval <= std::numeric_limits::max(); + case double_type : + default : + return false; + } +} + +bool web::json::number::is_int64() const +{ + switch (m_type) + { + case signed_type : return true; + case unsigned_type : return m_uintval <= static_cast(std::numeric_limits::max()); + case double_type : + default : + return false; + } +} + +bool web::json::details::_String::has_escape_chars(const _String &str) +{ + return std::any_of(std::begin(str.m_string), std::end(str.m_string), [](utility::string_t::value_type const x) + { + if (x <= 31) { return true; } + if (x == '"') { return true; } + if (x == '\\') { return true; } + return false; + }); +} + +web::json::value::value_type json::value::type() const { return m_value->type(); } + +bool json::value::is_integer() const +{ + if(!is_number()) + { + return false; + } + return m_value->is_integer(); +} + +bool json::value::is_double() const +{ + if(!is_number()) + { + return false; + } + return m_value->is_double(); +} + +json::value& web::json::details::_Object::index(const utility::string_t &key) +{ + return m_object[key]; +} + +bool web::json::details::_Object::has_field(const utility::string_t &key) const +{ + return m_object.find(key) != m_object.end(); +} + +utility::string_t json::value::to_string() const +{ +#ifndef _WIN32 + utility::details::scoped_c_thread_locale locale; +#endif + return m_value->to_string(); +} + +bool json::value::operator==(const json::value &other) const +{ + if (this->m_value.get() == other.m_value.get()) + return true; + if (this->type() != other.type()) + return false; + + switch(this->type()) + { + case Null: + return true; + case Number: + return this->as_number() == other.as_number(); + case Boolean: + return this->as_bool() == other.as_bool(); + case String: + return this->as_string() == other.as_string(); + case Object: + return static_cast(this->m_value.get())->is_equal(static_cast(other.m_value.get())); + case Array: + return static_cast(this->m_value.get())->is_equal(static_cast(other.m_value.get())); + } + __assume(0); +} + +void web::json::value::erase(size_t index) +{ + return this->as_array().erase(index); +} + +void web::json::value::erase(const utility::string_t &key) +{ + return this->as_object().erase(key); +} + +// at() overloads +web::json::value& web::json::value::at(size_t index) +{ + return this->as_array().at(index); +} + +const web::json::value& web::json::value::at(size_t index) const +{ + return this->as_array().at(index); +} + +web::json::value& web::json::value::at(const utility::string_t& key) +{ + return this->as_object().at(key); +} + +const web::json::value& web::json::value::at(const utility::string_t& key) const +{ + return this->as_object().at(key); +} + +web::json::value& web::json::value::operator [] (const utility::string_t &key) +{ + if ( this->is_null() ) + { + m_value.reset(new web::json::details::_Object(details::g_keep_json_object_unsorted)); +#ifdef ENABLE_JSON_VALUE_VISUALIZER + m_kind = value::Object; +#endif + } + return m_value->index(key); +} + +web::json::value& web::json::value::operator[](size_t index) +{ + if ( this->is_null() ) + { + m_value.reset(new web::json::details::_Array()); +#ifdef ENABLE_JSON_VALUE_VISUALIZER + m_kind = value::Array; +#endif + } + return m_value->index(index); +} + +// Remove once VS 2013 is no longer supported. +#if defined(_WIN32) && _MSC_VER < 1900 +static web::json::details::json_error_category_impl instance; +#endif +const web::json::details::json_error_category_impl& web::json::details::json_error_category() +{ +#if !defined(_WIN32) || _MSC_VER >= 1900 + static web::json::details::json_error_category_impl instance; +#endif + return instance; +} diff --git a/src/corehost/cli/json/casablanca/src/json/json_parsing.cpp b/src/corehost/cli/json/casablanca/src/json/json_parsing.cpp new file mode 100644 index 000000000..1cf40dc67 --- /dev/null +++ b/src/corehost/cli/json/casablanca/src/json/json_parsing.cpp @@ -0,0 +1,1312 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: JSON parser +* +* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#include "stdafx.h" +#include + +#if defined(_MSC_VER) +#pragma warning(disable : 4127) // allow expressions like while(true) pass +#endif +using namespace web; +using namespace web::json; +using namespace utility; +using namespace utility::conversions; + +std::array _hexval = {{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }}; + +namespace web { +namespace json +{ +namespace details +{ + +// +// JSON Parsing +// + +template +#if defined(_WIN32) + __declspec(noreturn) +#else + __attribute__((noreturn)) +#endif +void CreateException(const Token &tk, const utility::string_t &message) +{ + utility::ostringstream_t os; + os << _XPLATSTR("* Line ") << tk.start.m_line << _XPLATSTR(", Column ") << tk.start.m_column << _XPLATSTR(" Syntax error: ") << message; + utility::string_t osStr = os.str(); + throw web::json::json_exception(osStr.c_str()); +} + +template +void SetErrorCode(Token &tk, json_error jsonErrorCode) +{ + tk.m_error = std::error_code(jsonErrorCode, json_error_category()); +} + +template +class JSON_Parser +{ +public: + JSON_Parser() + : m_currentLine(1), + m_currentColumn(1), + m_currentParsingDepth(0) + { } + + struct Location + { + size_t m_line; + size_t m_column; + }; + + struct Token + { + enum Kind + { + TKN_EOF, + + TKN_OpenBrace, + TKN_CloseBrace, + TKN_OpenBracket, + TKN_CloseBracket, + TKN_Comma, + TKN_Colon, + TKN_StringLiteral, + TKN_NumberLiteral, + TKN_IntegerLiteral, + TKN_BooleanLiteral, + TKN_NullLiteral, + TKN_Comment + }; + + Token() : kind(TKN_EOF) {} + + Kind kind; + std::basic_string string_val; + + typename JSON_Parser::Location start; + + union + { + double double_val; + int64_t int64_val; + uint64_t uint64_val; + bool boolean_val; + bool has_unescape_symbol; + }; + + bool signed_number; + + std::error_code m_error; + }; + + void GetNextToken(Token &); + + web::json::value ParseValue(typename JSON_Parser::Token &first) + { +#ifndef _WIN32 + utility::details::scoped_c_thread_locale locale; +#endif + +#ifdef ENABLE_JSON_VALUE_VISUALIZER + auto _value = _ParseValue(first); + auto type = _value->type(); + return web::json::value(std::move(_value), type); +#else + return web::json::value(_ParseValue(first)); +#endif + } + +protected: + typedef typename std::char_traits::int_type int_type; + virtual int_type NextCharacter() = 0; + virtual int_type PeekCharacter() = 0; + + virtual bool CompleteComment(Token &token); + virtual bool CompleteStringLiteral(Token &token); + bool handle_unescape_char(Token &token); + +private: + + bool CompleteNumberLiteral(CharType first, Token &token); + bool ParseInt64(CharType first, uint64_t& value); + bool CompleteKeywordTrue(Token &token); + bool CompleteKeywordFalse(Token &token); + bool CompleteKeywordNull(Token &token); + std::unique_ptr _ParseValue(typename JSON_Parser::Token &first); + std::unique_ptr _ParseObject(typename JSON_Parser::Token &tkn); + std::unique_ptr _ParseArray(typename JSON_Parser::Token &tkn); + + JSON_Parser& operator=(const JSON_Parser&); + + int_type EatWhitespace(); + + void CreateToken(typename JSON_Parser::Token& tk, typename Token::Kind kind, Location &start) + { + tk.kind = kind; + tk.start = start; + tk.string_val.clear(); + } + + void CreateToken(typename JSON_Parser::Token& tk, typename Token::Kind kind) + { + tk.kind = kind; + tk.start.m_line = m_currentLine; + tk.start.m_column = m_currentColumn; + tk.string_val.clear(); + } + +protected: + + size_t m_currentLine; + size_t m_currentColumn; + size_t m_currentParsingDepth; + +// The DEBUG macro is defined in XCode but we don't in our CMakeList +// so for now we will keep the same on debug and release. In the future +// this can be increase on release if necessary. +#if defined(__APPLE__) + static const size_t maxParsingDepth = 32; +#else + static const size_t maxParsingDepth = 128; +#endif +}; + +// Replace with template alias once VS 2012 support is removed. +template +typename std::char_traits::int_type eof() +{ + return std::char_traits::eof(); +} + +template +class JSON_StreamParser : public JSON_Parser + { +public: + JSON_StreamParser(std::basic_istream &stream) + : m_streambuf(stream.rdbuf()) + { + } + +protected: + + virtual typename JSON_Parser::int_type NextCharacter(); + virtual typename JSON_Parser::int_type PeekCharacter(); + +private: + typename std::basic_streambuf>* m_streambuf; +}; + +template +class JSON_StringParser : public JSON_Parser +{ +public: + JSON_StringParser(const std::basic_string& string) + : m_position(&string[0]) + { + m_startpos = m_position; + m_endpos = m_position+string.size(); + } + +protected: + + virtual typename JSON_Parser::int_type NextCharacter(); + virtual typename JSON_Parser::int_type PeekCharacter(); + + virtual bool CompleteComment(typename JSON_Parser::Token &token); + virtual bool CompleteStringLiteral(typename JSON_Parser::Token &token); + +private: + bool finish_parsing_string_with_unescape_char(typename JSON_Parser::Token &token); + const CharType* m_position; + const CharType* m_startpos; + const CharType* m_endpos; +}; + + +template +typename JSON_Parser::int_type JSON_StreamParser::NextCharacter() +{ + auto ch = m_streambuf->sbumpc(); + + if (ch == '\n') + { + this->m_currentLine += 1; + this->m_currentColumn = 0; + } + else + { + this->m_currentColumn += 1; + } + + return ch; +} + +template +typename JSON_Parser::int_type JSON_StreamParser::PeekCharacter() +{ + return m_streambuf->sgetc(); +} + +template +typename JSON_Parser::int_type JSON_StringParser::NextCharacter() +{ + if (m_position == m_endpos) + return eof(); + + CharType ch = *m_position; + m_position += 1; + + if ( ch == '\n' ) + { + this->m_currentLine += 1; + this->m_currentColumn = 0; + } + else + { + this->m_currentColumn += 1; + } + + return ch; +} + +template +typename JSON_Parser::int_type JSON_StringParser::PeekCharacter() +{ + if ( m_position == m_endpos ) return eof(); + + return *m_position; +} + +// +// Consume whitespace characters and return the first non-space character or EOF +// +template +typename JSON_Parser::int_type JSON_Parser::EatWhitespace() +{ + auto ch = NextCharacter(); + + while ( ch != eof() && iswspace(static_cast(ch))) + { + ch = NextCharacter(); + } + + return ch; +} + +template +bool JSON_Parser::CompleteKeywordTrue(Token &token) +{ + if (NextCharacter() != 'r') + return false; + if (NextCharacter() != 'u') + return false; + if (NextCharacter() != 'e') + return false; + token.kind = Token::TKN_BooleanLiteral; + token.boolean_val = true; + return true; +} + +template +bool JSON_Parser::CompleteKeywordFalse(Token &token) +{ + if (NextCharacter() != 'a') + return false; + if (NextCharacter() != 'l') + return false; + if (NextCharacter() != 's') + return false; + if (NextCharacter() != 'e') + return false; + token.kind = Token::TKN_BooleanLiteral; + token.boolean_val = false; + return true; +} + +template +bool JSON_Parser::CompleteKeywordNull(Token &token) +{ + if (NextCharacter() != 'u') + return false; + if (NextCharacter() != 'l') + return false; + if (NextCharacter() != 'l') + return false; + token.kind = Token::TKN_NullLiteral; + return true; +} + +// Returns false only on overflow +template +inline bool JSON_Parser::ParseInt64(CharType first, uint64_t& value) +{ + value = first - '0'; + auto ch = PeekCharacter(); + while (ch >= '0' && ch <= '9') + { + unsigned int next_digit = (unsigned int)(ch - '0'); + if (value > (ULLONG_MAX / 10) || (value == ULLONG_MAX/10 && next_digit > ULLONG_MAX%10)) + return false; + + NextCharacter(); + + value *= 10; + value += next_digit; + ch = PeekCharacter(); + } + return true; +} + +// This namespace hides the x-plat helper functions +namespace +{ +#if defined(_WIN32) + static int print_llu(char* ptr, size_t n, uint64_t val64) + { + return _snprintf_s_l(ptr, n, _TRUNCATE, "%I64u", utility::details::scoped_c_thread_locale::c_locale(), val64); + } + + static int print_llu(wchar_t* ptr, size_t n, uint64_t val64) + { + return _snwprintf_s_l(ptr, n, _TRUNCATE, L"%I64u", utility::details::scoped_c_thread_locale::c_locale(), val64); + } + static double anystod(const char* str) + { + return _strtod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale()); + } + static double anystod(const wchar_t* str) + { + return _wcstod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale()); + } +#else + static int __attribute__((__unused__)) print_llu(char* ptr, size_t n, unsigned long long val64) + { + return snprintf(ptr, n, "%llu", val64); + } + static int __attribute__((__unused__)) print_llu(char* ptr, size_t n, unsigned long val64) + { + return snprintf(ptr, n, "%lu", val64); + } + static double __attribute__((__unused__)) anystod(const char* str) + { + return strtod(str, nullptr); + } + static double __attribute__((__unused__)) anystod(const wchar_t* str) + { + return wcstod(str, nullptr); + } +#endif +} + +template +bool JSON_Parser::CompleteNumberLiteral(CharType first, Token &token) +{ + bool minus_sign; + + if (first == '-') + { + minus_sign = true; + + // Safe to cast because the check after this if/else statement will cover EOF. + first = static_cast(NextCharacter()); + } + else + { + minus_sign = false; + } + + if (first < '0' || first > '9') + return false; + + auto ch = PeekCharacter(); + + //Check for two (or more) zeros at the beginning + if (first == '0' && ch == '0') + return false; + + // Parse the number assuming its integer + uint64_t val64; + bool complete = ParseInt64(first, val64); + + ch = PeekCharacter(); + if (complete && ch!='.' && ch!='E' && ch!='e') + { + if (minus_sign) + { + if (val64 > static_cast(1) << 63 ) + { + // It is negative and cannot be represented in int64, so we resort to double + token.double_val = 0 - static_cast(val64); + token.signed_number = true; + token.kind = JSON_Parser::Token::TKN_NumberLiteral; + return true; + } + + // It is negative, but fits into int64 + token.int64_val = 0 - static_cast(val64); + token.kind = JSON_Parser::Token::TKN_IntegerLiteral; + token.signed_number = true; + return true; + } + + // It is positive so we use unsigned int64 + token.uint64_val = val64; + token.kind = JSON_Parser::Token::TKN_IntegerLiteral; + token.signed_number = false; + return true; + } + + // Magic number 5 leaves room for decimal point, null terminator, etc (in most cases) + ::std::vector buf(::std::numeric_limits::digits10 + 5); + int count = print_llu(buf.data(), buf.size(), val64); + _ASSERTE(count >= 0); + _ASSERTE((size_t)count < buf.size()); + // Resize to cut off the null terminator + buf.resize(count); + + bool decimal = false; + + while (ch != eof()) + { + // Digit encountered? + if (ch >= '0' && ch <= '9') + { + buf.push_back(static_cast(ch)); + NextCharacter(); + ch = PeekCharacter(); + } + + // Decimal dot? + else if (ch == '.') + { + if (decimal) + return false; + + decimal = true; + buf.push_back(static_cast(ch)); + + NextCharacter(); + ch = PeekCharacter(); + + // Check that the following char is a digit + if (ch < '0' || ch > '9') + return false; + + buf.push_back(static_cast(ch)); + NextCharacter(); + ch = PeekCharacter(); + } + + // Exponent? + else if (ch == 'E' || ch == 'e') + { + buf.push_back(static_cast(ch)); + NextCharacter(); + ch = PeekCharacter(); + + // Check for the exponent sign + if (ch == '+') + { + buf.push_back(static_cast(ch)); + NextCharacter(); + ch = PeekCharacter(); + } + else if (ch == '-') + { + buf.push_back(static_cast(ch)); + NextCharacter(); + ch = PeekCharacter(); + } + + // First number of the exponent + if (ch >= '0' && ch <= '9') + { + buf.push_back(static_cast(ch)); + NextCharacter(); + ch = PeekCharacter(); + } + else return false; + + // The rest of the exponent + while (ch >= '0' && ch <= '9') + { + buf.push_back(static_cast(ch)); + NextCharacter(); + ch = PeekCharacter(); + } + + // The peeked character is not a number, so we can break from the loop and construct the number + break; + } + else + { + // Not expected number character? + break; + } + }; + + buf.push_back('\0'); + token.double_val = anystod(buf.data()); + if (minus_sign) + { + token.double_val = -token.double_val; + } + token.kind = (JSON_Parser::Token::TKN_NumberLiteral); + + return true; +} + +template +bool JSON_Parser::CompleteComment(Token &token) +{ + // We already found a '/' character as the first of a token -- what kind of comment is it? + + auto ch = NextCharacter(); + + if ( ch == eof() || (ch != '/' && ch != '*') ) + return false; + + if ( ch == '/' ) + { + // Line comment -- look for a newline or EOF to terminate. + + ch = NextCharacter(); + + while ( ch != eof() && ch != '\n') + { + ch = NextCharacter(); + } + } + else + { + // Block comment -- look for a terminating "*/" sequence. + + ch = NextCharacter(); + + while ( true ) + { + if ( ch == eof()) + return false; + + if ( ch == '*' ) + { + auto ch1 = PeekCharacter(); + + if ( ch1 == eof()) + return false; + + if ( ch1 == '/' ) + { + // Consume the character + NextCharacter(); + break; + } + + ch = ch1; + } + + ch = NextCharacter(); + } + } + + token.kind = Token::TKN_Comment; + + return true; +} + +template +bool JSON_StringParser::CompleteComment(typename JSON_Parser::Token &token) +{ + // This function is specialized for the string parser, since we can be slightly more + // efficient in copying data from the input to the token: do a memcpy() rather than + // one character at a time. + + auto ch = JSON_StringParser::NextCharacter(); + + if ( ch == eof() || (ch != '/' && ch != '*') ) + return false; + + if ( ch == '/' ) + { + // Line comment -- look for a newline or EOF to terminate. + + ch = JSON_StringParser::NextCharacter(); + + while ( ch != eof() && ch != '\n') + { + ch = JSON_StringParser::NextCharacter(); + } + } + else + { + // Block comment -- look for a terminating "*/" sequence. + + ch = JSON_StringParser::NextCharacter(); + + while ( true ) + { + if ( ch == eof()) + return false; + + if ( ch == '*' ) + { + ch = JSON_StringParser::PeekCharacter(); + + if ( ch == eof()) + return false; + + if ( ch == '/' ) + { + // Consume the character + JSON_StringParser::NextCharacter(); + break; + } + + } + + ch = JSON_StringParser::NextCharacter(); + } + } + + token.kind = JSON_Parser::Token::TKN_Comment; + + return true; +} + +void convert_append_unicode_code_unit(JSON_Parser::Token &token, utf16char value) +{ + token.string_val.push_back(value); +} +void convert_append_unicode_code_unit(JSON_Parser::Token &token, utf16char value) +{ + utf16string utf16(reinterpret_cast(&value), 1); + token.string_val.append(::utility::conversions::utf16_to_utf8(utf16)); +} + +template +inline bool JSON_Parser::handle_unescape_char(Token &token) +{ + token.has_unescape_symbol = true; + + // This function converts unescaped character pairs (e.g. "\t") into their ASCII or Unicode representations (e.g. tab sign) + // Also it handles \u + 4 hexadecimal digits + auto ch = NextCharacter(); + switch (ch) + { + case '\"': + token.string_val.push_back('\"'); + return true; + case '\\': + token.string_val.push_back('\\'); + return true; + case '/': + token.string_val.push_back('/'); + return true; + case 'b': + token.string_val.push_back('\b'); + return true; + case 'f': + token.string_val.push_back('\f'); + return true; + case 'r': + token.string_val.push_back('\r'); + return true; + case 'n': + token.string_val.push_back('\n'); + return true; + case 't': + token.string_val.push_back('\t'); + return true; + case 'u': + { + // A four-hexdigit Unicode character. + // Transform into a 16 bit code point. + int decoded = 0; + for (int i = 0; i < 4; ++i) + { + ch = NextCharacter(); + int ch_int = static_cast(ch); + if (ch_int < 0 || ch_int > 127) + return false; +#ifdef _WIN32 + const int isxdigitResult = _isxdigit_l(ch_int, utility::details::scoped_c_thread_locale::c_locale()); +#else + const int isxdigitResult = isxdigit(ch_int); +#endif + if (!isxdigitResult) + return false; + + int val = _hexval[static_cast(ch_int)]; + _ASSERTE(val != -1); + + // Add the input char to the decoded number + decoded |= (val << (4 * (3 - i))); + } + + // Construct the character based on the decoded number + convert_append_unicode_code_unit(token, static_cast(decoded)); + + return true; + } + default: + return false; + } +} + +template +bool JSON_Parser::CompleteStringLiteral(Token &token) +{ + token.has_unescape_symbol = false; + auto ch = NextCharacter(); + while ( ch != '"' ) + { + if ( ch == '\\' ) + { + handle_unescape_char(token); + } + else if (ch >= CharType(0x0) && ch < CharType(0x20)) + { + return false; + } + else + { + if (ch == eof()) + return false; + + token.string_val.push_back(static_cast(ch)); + } + ch = NextCharacter(); + } + + if ( ch == '"' ) + { + token.kind = Token::TKN_StringLiteral; + } + else + { + return false; + } + + return true; +} + +template +bool JSON_StringParser::CompleteStringLiteral(typename JSON_Parser::Token &token) +{ + // This function is specialized for the string parser, since we can be slightly more + // efficient in copying data from the input to the token: do a memcpy() rather than + // one character at a time. + + auto start = m_position; + token.has_unescape_symbol = false; + + auto ch = JSON_StringParser::NextCharacter(); + + while (ch != '"') + { + if (ch == eof()) + return false; + + if (ch == '\\') + { + const size_t numChars = m_position - start - 1; + const size_t prevSize = token.string_val.size(); + token.string_val.resize(prevSize + numChars); + memcpy(const_cast(token.string_val.c_str() + prevSize), start, numChars * sizeof(CharType)); + + if (!JSON_StringParser::handle_unescape_char(token)) + { + return false; + } + + // Reset start position and continue. + start = m_position; + } + else if (ch >= CharType(0x0) && ch < CharType(0x20)) + { + return false; + } + + ch = JSON_StringParser::NextCharacter(); + } + + const size_t numChars = m_position - start - 1; + const size_t prevSize = token.string_val.size(); + token.string_val.resize(prevSize + numChars); + memcpy(const_cast(token.string_val.c_str() + prevSize), start, numChars * sizeof(CharType)); + + token.kind = JSON_Parser::Token::TKN_StringLiteral; + + return true; +} + +template +void JSON_Parser::GetNextToken(typename JSON_Parser::Token& result) +{ +try_again: + auto ch = EatWhitespace(); + + CreateToken(result, Token::TKN_EOF); + + if (ch == eof()) return; + + switch (ch) + { + case '{': + case '[': + { + if(++m_currentParsingDepth > JSON_Parser::maxParsingDepth) + { + SetErrorCode(result, json_error::nesting); + break; + } + + typename JSON_Parser::Token::Kind tk = ch == '{' ? Token::TKN_OpenBrace : Token::TKN_OpenBracket; + CreateToken(result, tk, result.start); + break; + } + case '}': + case ']': + { + if((signed int)(--m_currentParsingDepth) < 0) + { + SetErrorCode(result, json_error::mismatched_brances); + break; + } + + typename JSON_Parser::Token::Kind tk = ch == '}' ? Token::TKN_CloseBrace : Token::TKN_CloseBracket; + CreateToken(result, tk, result.start); + break; + } + case ',': + CreateToken(result, Token::TKN_Comma, result.start); + break; + + case ':': + CreateToken(result, Token::TKN_Colon, result.start); + break; + + case 't': + if (!CompleteKeywordTrue(result)) + { + SetErrorCode(result, json_error::malformed_literal); + } + break; + case 'f': + if (!CompleteKeywordFalse(result)) + { + SetErrorCode(result, json_error::malformed_literal); + } + break; + case 'n': + if (!CompleteKeywordNull(result)) + { + SetErrorCode(result, json_error::malformed_literal); + } + break; + case '/': + if (!CompleteComment(result)) + { + SetErrorCode(result, json_error::malformed_comment); + break; + } + // For now, we're ignoring comments. + goto try_again; + case '"': + if (!CompleteStringLiteral(result)) + { + SetErrorCode(result, json_error::malformed_string_literal); + } + break; + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!CompleteNumberLiteral(static_cast(ch), result)) + { + SetErrorCode(result, json_error::malformed_numeric_literal); + } + break; + default: + SetErrorCode(result, json_error::malformed_token); + break; + } +} + +template +std::unique_ptr JSON_Parser::_ParseObject(typename JSON_Parser::Token &tkn) +{ + auto obj = utility::details::make_unique(g_keep_json_object_unsorted); + auto& elems = obj->m_object.m_elements; + + GetNextToken(tkn); + if (tkn.m_error) goto error; + + if (tkn.kind != JSON_Parser::Token::TKN_CloseBrace) + { + while (true) + { + // State 1: New field or end of object, looking for field name or closing brace + std::basic_string fieldName; + switch (tkn.kind) + { + case JSON_Parser::Token::TKN_StringLiteral: + fieldName = std::move(tkn.string_val); + break; + default: + goto error; + } + + GetNextToken(tkn); + if (tkn.m_error) goto error; + + // State 2: Looking for a colon. + if (tkn.kind != JSON_Parser::Token::TKN_Colon) goto done; + + GetNextToken(tkn); + if (tkn.m_error) goto error; + + // State 3: Looking for an expression. +#ifdef ENABLE_JSON_VALUE_VISUALIZER + auto fieldValue = _ParseValue(tkn); + auto type = fieldValue->type(); + elems.emplace_back(utility::conversions::to_string_t(std::move(fieldName)), json::value(std::move(fieldValue), type)); +#else + elems.emplace_back(utility::conversions::to_string_t(std::move(fieldName)), json::value(_ParseValue(tkn))); +#endif + if (tkn.m_error) goto error; + + // State 4: Looking for a comma or a closing brace + switch (tkn.kind) + { + case JSON_Parser::Token::TKN_Comma: + GetNextToken(tkn); + if (tkn.m_error) goto error; + break; + case JSON_Parser::Token::TKN_CloseBrace: + goto done; + default: + goto error; + } + } + } + +done: + GetNextToken(tkn); + if (tkn.m_error) return utility::details::make_unique(); + + if (!g_keep_json_object_unsorted) { + ::std::sort(elems.begin(), elems.end(), json::object::compare_pairs); + } + + return std::move(obj); + +error: + if (!tkn.m_error) + { + SetErrorCode(tkn, json_error::malformed_object_literal); + } + return utility::details::make_unique(); +} + +template +std::unique_ptr JSON_Parser::_ParseArray(typename JSON_Parser::Token &tkn) +{ + GetNextToken(tkn); + if (tkn.m_error) return utility::details::make_unique(); + + auto result = utility::details::make_unique(); + + if (tkn.kind != JSON_Parser::Token::TKN_CloseBracket) + { + while (true) + { + // State 1: Looking for an expression. + result->m_array.m_elements.emplace_back(ParseValue(tkn)); + if (tkn.m_error) return utility::details::make_unique(); + + // State 4: Looking for a comma or a closing bracket + switch (tkn.kind) + { + case JSON_Parser::Token::TKN_Comma: + GetNextToken(tkn); + if (tkn.m_error) return utility::details::make_unique(); + break; + case JSON_Parser::Token::TKN_CloseBracket: + GetNextToken(tkn); + if (tkn.m_error) return utility::details::make_unique(); + return std::move(result); + default: + SetErrorCode(tkn, json_error::malformed_array_literal); + return utility::details::make_unique(); + } + } + } + + GetNextToken(tkn); + if (tkn.m_error) return utility::details::make_unique(); + + return std::move(result); +} + +template +std::unique_ptr JSON_Parser::_ParseValue(typename JSON_Parser::Token &tkn) +{ + switch (tkn.kind) + { + case JSON_Parser::Token::TKN_OpenBrace: + { + return _ParseObject(tkn); + } + case JSON_Parser::Token::TKN_OpenBracket: + { + return _ParseArray(tkn); + } + case JSON_Parser::Token::TKN_StringLiteral: + { + auto value = utility::details::make_unique(std::move(tkn.string_val), tkn.has_unescape_symbol); + GetNextToken(tkn); + if (tkn.m_error) return utility::details::make_unique(); + return std::move(value); + } + case JSON_Parser::Token::TKN_IntegerLiteral: + { + std::unique_ptr value; + if (tkn.signed_number) + value = utility::details::make_unique(tkn.int64_val); + else + value = utility::details::make_unique(tkn.uint64_val); + + GetNextToken(tkn); + if (tkn.m_error) return utility::details::make_unique(); + return std::move(value); + } + case JSON_Parser::Token::TKN_NumberLiteral: + { + auto value = utility::details::make_unique(tkn.double_val); + GetNextToken(tkn); + if (tkn.m_error) return utility::details::make_unique(); + return std::move(value); + } + case JSON_Parser::Token::TKN_BooleanLiteral: + { + auto value = utility::details::make_unique(tkn.boolean_val); + GetNextToken(tkn); + if (tkn.m_error) return utility::details::make_unique(); + return std::move(value); + } + case JSON_Parser::Token::TKN_NullLiteral: + { + GetNextToken(tkn); + // Returning a null value whether or not an error occurred. + return utility::details::make_unique(); + } + default: + { + SetErrorCode(tkn, json_error::malformed_token); + return utility::details::make_unique(); + } + } +} + +}}} + +static web::json::value _parse_stream(utility::istream_t &stream) +{ + web::json::details::JSON_StreamParser parser(stream); + web::json::details::JSON_Parser::Token tkn; + + parser.GetNextToken(tkn); + if (tkn.m_error) + { + web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); + } + + auto value = parser.ParseValue(tkn); + if (tkn.m_error) + { + web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); + } + else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) + { + web::json::details::CreateException(tkn, _XPLATSTR("Left-over characters in stream after parsing a JSON value")); + } + return value; +} + +static web::json::value _parse_stream(utility::istream_t &stream, std::error_code& error) +{ + web::json::details::JSON_StreamParser parser(stream); + web::json::details::JSON_Parser::Token tkn; + + parser.GetNextToken(tkn); + if (tkn.m_error) + { + error = std::move(tkn.m_error); + return web::json::value(); + } + + auto returnObject = parser.ParseValue(tkn); + if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) + { + web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream); + } + + error = std::move(tkn.m_error); + return returnObject; +} + +#ifdef _WIN32 +static web::json::value _parse_narrow_stream(std::istream &stream) +{ + web::json::details::JSON_StreamParser parser(stream); + web::json::details::JSON_StreamParser::Token tkn; + + parser.GetNextToken(tkn); + if (tkn.m_error) + { + web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); + } + + auto value = parser.ParseValue(tkn); + if (tkn.m_error) + { + web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); + } + else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) + { + web::json::details::CreateException(tkn, _XPLATSTR("Left-over characters in stream after parsing a JSON value")); + } + return value; +} + +static web::json::value _parse_narrow_stream(std::istream &stream, std::error_code& error) +{ + web::json::details::JSON_StreamParser parser(stream); + web::json::details::JSON_StreamParser::Token tkn; + + parser.GetNextToken(tkn); + if (tkn.m_error) + { + error = std::move(tkn.m_error); + return web::json::value(); + } + + auto returnObject = parser.ParseValue(tkn); + if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) + { + returnObject = web::json::value(); + web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream); + } + + error = std::move(tkn.m_error); + return returnObject; +} +#endif + +web::json::value web::json::value::parse(const utility::string_t& str) +{ + web::json::details::JSON_StringParser parser(str); + web::json::details::JSON_Parser::Token tkn; + + parser.GetNextToken(tkn); + if (tkn.m_error) + { + web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); + } + + auto value = parser.ParseValue(tkn); + if (tkn.m_error) + { + web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); + } + else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) + { + web::json::details::CreateException(tkn, _XPLATSTR("Left-over characters in stream after parsing a JSON value")); + } + return value; +} + +web::json::value web::json::value::parse(const utility::string_t& str, std::error_code& error) +{ + web::json::details::JSON_StringParser parser(str); + web::json::details::JSON_Parser::Token tkn; + + parser.GetNextToken(tkn); + if (tkn.m_error) + { + error = std::move(tkn.m_error); + return web::json::value(); + } + + auto returnObject = parser.ParseValue(tkn); + if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) + { + returnObject = web::json::value(); + web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream); + } + + error = std::move(tkn.m_error); + return returnObject; +} + +web::json::value web::json::value::parse(utility::istream_t &stream) +{ + return _parse_stream(stream); +} + +web::json::value web::json::value::parse(utility::istream_t &stream, std::error_code& error) +{ + return _parse_stream(stream, error); +} + +#ifdef _WIN32 +web::json::value web::json::value::parse(std::istream& stream) +{ + return _parse_narrow_stream(stream); +} + +web::json::value web::json::value::parse(std::istream& stream, std::error_code& error) +{ + return _parse_narrow_stream(stream, error); +} +#endif diff --git a/src/corehost/cli/json/casablanca/src/json/json_serialization.cpp b/src/corehost/cli/json/casablanca/src/json/json_serialization.cpp new file mode 100644 index 000000000..e99e0bc5a --- /dev/null +++ b/src/corehost/cli/json/casablanca/src/json/json_serialization.cpp @@ -0,0 +1,274 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: JSON parser and writer +* +* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#include "stdafx.h" +#include + +#ifndef _WIN32 +#define __STDC_FORMAT_MACROS +#include +#endif + +using namespace web; +using namespace web::json; +using namespace utility; +using namespace utility::conversions; + +// +// JSON Serialization +// + +#ifdef _WIN32 +void web::json::value::serialize(std::ostream& stream) const +{ + // This has better performance than writing directly to stream. + std::string str; + m_value->serialize_impl(str); + stream << str; +} +void web::json::value::format(std::basic_string &string) const +{ + m_value->format(string); +} +#endif + +void web::json::value::serialize(utility::ostream_t &stream) const +{ +#ifndef _WIN32 + utility::details::scoped_c_thread_locale locale; +#endif + + // This has better performance than writing directly to stream. + utility::string_t str; + m_value->serialize_impl(str); + stream << str; +} + +void web::json::value::format(std::basic_string& string) const +{ + m_value->format(string); +} + +template +void web::json::details::append_escape_string(std::basic_string& str, const std::basic_string& escaped) +{ + for (const auto &ch : escaped) + { + switch (ch) + { + case '\"': + str += '\\'; + str += '\"'; + break; + case '\\': + str += '\\'; + str += '\\'; + break; + case '\b': + str += '\\'; + str += 'b'; + break; + case '\f': + str += '\\'; + str += 'f'; + break; + case '\r': + str += '\\'; + str += 'r'; + break; + case '\n': + str += '\\'; + str += 'n'; + break; + case '\t': + str += '\\'; + str += 't'; + break; + default: + + // If a control character then must unicode escaped. + if (ch >= 0 && ch <= 0x1F) + { + static const std::array intToHex = { { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' } }; + str += '\\'; + str += 'u'; + str += '0'; + str += '0'; + str += intToHex[(ch & 0xF0) >> 4]; + str += intToHex[ch & 0x0F]; + } + else + { + str += ch; + } + } + } +} + +void web::json::details::format_string(const utility::string_t& key, utility::string_t& str) +{ + str.push_back('"'); + append_escape_string(str, key); + str.push_back('"'); +} + +#ifdef _WIN32 +void web::json::details::format_string(const utility::string_t& key, std::string& str) +{ + str.push_back('"'); + append_escape_string(str, utility::conversions::to_utf8string(key)); + str.push_back('"'); +} +#endif + +void web::json::details::_String::format(std::basic_string& str) const +{ + str.push_back('"'); + + if(m_has_escape_char) + { + append_escape_string(str, utility::conversions::to_utf8string(m_string)); + } + else + { + str.append(utility::conversions::to_utf8string(m_string)); + } + + str.push_back('"'); +} + +void web::json::details::_Number::format(std::basic_string& stream) const +{ + if(m_number.m_type != number::type::double_type) + { + // #digits + 1 to avoid loss + 1 for the sign + 1 for null terminator. + const size_t tempSize = std::numeric_limits::digits10 + 3; + char tempBuffer[tempSize]; + +#ifdef _WIN32 + // This can be improved performance-wise if we implement our own routine + if (m_number.m_type == number::type::signed_type) + _i64toa_s(m_number.m_intval, tempBuffer, tempSize, 10); + else + _ui64toa_s(m_number.m_uintval, tempBuffer, tempSize, 10); + + const auto numChars = strnlen_s(tempBuffer, tempSize); +#else + int numChars; + if (m_number.m_type == number::type::signed_type) + numChars = snprintf(tempBuffer, tempSize, "%" PRId64, m_number.m_intval); + else + numChars = snprintf(tempBuffer, tempSize, "%" PRIu64, m_number.m_uintval); +#endif + stream.append(tempBuffer, numChars); + } + else + { + // #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx) + 1 for null terminator + const size_t tempSize = std::numeric_limits::digits10 + 10; + char tempBuffer[tempSize]; +#ifdef _WIN32 + const auto numChars = _sprintf_s_l( + tempBuffer, + tempSize, + "%.*g", + utility::details::scoped_c_thread_locale::c_locale(), + std::numeric_limits::digits10 + 2, + m_number.m_value); +#else + const auto numChars = snprintf(tempBuffer, tempSize, "%.*g", std::numeric_limits::digits10 + 2, m_number.m_value); +#endif + stream.append(tempBuffer, numChars); + } +} + +#ifdef _WIN32 + +void web::json::details::_String::format(std::basic_string& str) const +{ + str.push_back(L'"'); + + if(m_has_escape_char) + { + append_escape_string(str, m_string); + } + else + { + str.append(m_string); + } + + str.push_back(L'"'); +} + +void web::json::details::_Number::format(std::basic_string& stream) const +{ + if(m_number.m_type != number::type::double_type) + { + // #digits + 1 to avoid loss + 1 for the sign + 1 for null terminator. + const size_t tempSize = std::numeric_limits::digits10 + 3; + wchar_t tempBuffer[tempSize]; + + if (m_number.m_type == number::type::signed_type) + _i64tow_s(m_number.m_intval, tempBuffer, tempSize, 10); + else + _ui64tow_s(m_number.m_uintval, tempBuffer, tempSize, 10); + + stream.append(tempBuffer, wcsnlen_s(tempBuffer, tempSize)); + } + else + { + // #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx) + 1 for null terminator + const size_t tempSize = std::numeric_limits::digits10 + 10; + wchar_t tempBuffer[tempSize]; + const int numChars = _swprintf_s_l( + tempBuffer, + tempSize, + L"%.*g", + utility::details::scoped_c_thread_locale::c_locale(), + std::numeric_limits::digits10 + 2, + m_number.m_value); + stream.append(tempBuffer, numChars); + } +} + +#endif + +const utility::string_t & web::json::details::_String::as_string() const +{ + return m_string; +} + +const utility::string_t & web::json::value::as_string() const +{ + return m_value->as_string(); +} + +utility::string_t json::value::serialize() const +{ +#ifndef _WIN32 + utility::details::scoped_c_thread_locale locale; +#endif + return m_value->to_string(); +} diff --git a/src/corehost/cli/json/casablanca/src/utilities/asyncrt_utils.cpp b/src/corehost/cli/json/casablanca/src/utilities/asyncrt_utils.cpp new file mode 100644 index 000000000..87a0d8957 --- /dev/null +++ b/src/corehost/cli/json/casablanca/src/utilities/asyncrt_utils.cpp @@ -0,0 +1,496 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Utilities +* +* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#include "stdafx.h" + +#ifndef _WIN32 +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-local-typedef" +#endif +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#endif + +// Could use C++ standard library if not __GLIBCXX__, +// For testing purposes we just the handwritten on all platforms. +#if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) +#include +#endif + +using namespace web; +using namespace utility; +using namespace utility::conversions; + +namespace utility +{ +namespace details +{ + +#if !defined(ANDROID) && !defined(__ANDROID__) +std::once_flag g_c_localeFlag; +std::unique_ptr g_c_locale(nullptr, [](scoped_c_thread_locale::xplat_locale *){}); +scoped_c_thread_locale::xplat_locale scoped_c_thread_locale::c_locale() +{ + std::call_once(g_c_localeFlag, [&]() + { + scoped_c_thread_locale::xplat_locale *clocale = new scoped_c_thread_locale::xplat_locale(); +#ifdef _WIN32 + *clocale = _create_locale(LC_ALL, "C"); + if (*clocale == nullptr) + { + throw std::runtime_error("Unable to create 'C' locale."); + } + auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale) + { + _free_locale(*clocale); + delete clocale; + }; +#else + *clocale = newlocale(LC_ALL, "C", nullptr); + if (*clocale == nullptr) + { + throw std::runtime_error("Unable to create 'C' locale."); + } + auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale) + { + freelocale(*clocale); + delete clocale; + }; +#endif + g_c_locale = std::unique_ptr(clocale, deleter); + }); + return *g_c_locale; +} +#endif + +#ifdef _WIN32 +scoped_c_thread_locale::scoped_c_thread_locale() + : m_prevLocale(), m_prevThreadSetting(-1) +{ + char *prevLocale = setlocale(LC_ALL, nullptr); + if (prevLocale == nullptr) + { + throw std::runtime_error("Unable to retrieve current locale."); + } + + if (std::strcmp(prevLocale, "C") != 0) + { + m_prevLocale = prevLocale; + m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + if (m_prevThreadSetting == -1) + { + throw std::runtime_error("Unable to enable per thread locale."); + } + if (setlocale(LC_ALL, "C") == nullptr) + { + _configthreadlocale(m_prevThreadSetting); + throw std::runtime_error("Unable to set locale"); + } + } +} + +scoped_c_thread_locale::~scoped_c_thread_locale() +{ + if (m_prevThreadSetting != -1) + { + setlocale(LC_ALL, m_prevLocale.c_str()); + _configthreadlocale(m_prevThreadSetting); + } +} +#elif (defined(ANDROID) || defined(__ANDROID__)) +scoped_c_thread_locale::scoped_c_thread_locale() {} +scoped_c_thread_locale::~scoped_c_thread_locale() {} +#else +scoped_c_thread_locale::scoped_c_thread_locale() + : m_prevLocale(nullptr) +{ + char *prevLocale = setlocale(LC_ALL, nullptr); + if (prevLocale == nullptr) + { + throw std::runtime_error("Unable to retrieve current locale."); + } + + if (std::strcmp(prevLocale, "C") != 0) + { + m_prevLocale = uselocale(c_locale()); + if (m_prevLocale == nullptr) + { + throw std::runtime_error("Unable to set locale"); + } + } +} + +scoped_c_thread_locale::~scoped_c_thread_locale() +{ + if (m_prevLocale != nullptr) + { + uselocale(m_prevLocale); + } +} +#endif +} + +namespace details +{ + +const std::error_category & __cdecl platform_category() +{ +#ifdef _WIN32 + return windows_category(); +#else + return linux_category(); +#endif +} + +#ifdef _WIN32 + +// Remove once VS 2013 is no longer supported. +#if _MSC_VER < 1900 +static details::windows_category_impl instance; +#endif +const std::error_category & __cdecl windows_category() +{ +#if _MSC_VER >= 1900 + static details::windows_category_impl instance; +#endif + return instance; +} + +std::string windows_category_impl::message(int errorCode) const CPPREST_NOEXCEPT +{ + const size_t buffer_size = 4096; + DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM; + LPCVOID lpSource = NULL; + +#if !defined(__cplusplus_winrt) + if (errorCode >= 12000) + { + dwFlags = FORMAT_MESSAGE_FROM_HMODULE; + lpSource = GetModuleHandleA("winhttp.dll"); // this handle DOES NOT need to be freed + } +#endif + + std::wstring buffer; + buffer.resize(buffer_size); + + const auto result = ::FormatMessageW( + dwFlags, + lpSource, + errorCode, + 0, + &buffer[0], + buffer_size, + NULL); + if (result == 0) + { + std::ostringstream os; + os << "Unable to get an error message for error code: " << errorCode << "."; + return os.str(); + } + + return utility::conversions::to_utf8string(buffer); +} + +std::error_condition windows_category_impl::default_error_condition(int errorCode) const CPPREST_NOEXCEPT +{ + // First see if the STL implementation can handle the mapping for common cases. + const std::error_condition errCondition = std::system_category().default_error_condition(errorCode); + const std::string errConditionMsg = errCondition.message(); + if(_stricmp(errConditionMsg.c_str(), "unknown error") != 0) + { + return errCondition; + } + + switch(errorCode) + { +#ifndef __cplusplus_winrt + case ERROR_WINHTTP_TIMEOUT: + return std::errc::timed_out; + case ERROR_WINHTTP_CANNOT_CONNECT: + return std::errc::host_unreachable; + case ERROR_WINHTTP_CONNECTION_ERROR: + return std::errc::connection_aborted; +#endif + case INET_E_RESOURCE_NOT_FOUND: + case INET_E_CANNOT_CONNECT: + return std::errc::host_unreachable; + case INET_E_CONNECTION_TIMEOUT: + return std::errc::timed_out; + case INET_E_DOWNLOAD_FAILURE: + return std::errc::connection_aborted; + default: + break; + } + + return std::error_condition(errorCode, *this); +} + +#else + +const std::error_category & __cdecl linux_category() +{ + // On Linux we are using boost error codes which have the exact same + // mapping and are equivalent with std::generic_category error codes. + return std::generic_category(); +} + +#endif + +} + +#define LOW_3BITS 0x7 +#define LOW_4BITS 0xF +#define LOW_5BITS 0x1F +#define LOW_6BITS 0x3F +#define BIT4 0x8 +#define BIT5 0x10 +#define BIT6 0x20 +#define BIT7 0x40 +#define BIT8 0x80 +#define L_SURROGATE_START 0xDC00 +#define L_SURROGATE_END 0xDFFF +#define H_SURROGATE_START 0xD800 +#define H_SURROGATE_END 0xDBFF +#define SURROGATE_PAIR_START 0x10000 + +utf16string __cdecl conversions::utf8_to_utf16(const std::string &s) +{ +#if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) + std::wstring_convert, utf16char> conversion; + return conversion.from_bytes(src); +#else + utf16string dest; + // Save repeated heap allocations, use less than source string size assuming some + // of the characters are not just ASCII and collapse. + dest.reserve(static_cast(static_cast(s.size()) * .70)); + + for (auto src = s.begin(); src != s.end(); ++src) + { + if ((*src & BIT8) == 0) // single byte character, 0x0 to 0x7F + { + dest.push_back(utf16string::value_type(*src)); + } + else + { + unsigned char numContBytes = 0; + uint32_t codePoint; + if ((*src & BIT7) == 0) + { + throw std::range_error("UTF-8 string character can never start with 10xxxxxx"); + } + else if ((*src & BIT6) == 0) // 2 byte character, 0x80 to 0x7FF + { + codePoint = *src & LOW_5BITS; + numContBytes = 1; + } + else if ((*src & BIT5) == 0) // 3 byte character, 0x800 to 0xFFFF + { + codePoint = *src & LOW_4BITS; + numContBytes = 2; + } + else if ((*src & BIT4) == 0) // 4 byte character, 0x10000 to 0x10FFFF + { + codePoint = *src & LOW_3BITS; + numContBytes = 3; + } + else + { + throw std::range_error("UTF-8 string has invalid Unicode code point"); + } + + for (unsigned char i = 0; i < numContBytes; ++i) + { + if (++src == s.end()) + { + throw std::range_error("UTF-8 string is missing bytes in character"); + } + if ((*src & BIT8) == 0 || (*src & BIT7) != 0) + { + throw std::range_error("UTF-8 continuation byte is missing leading byte"); + } + codePoint <<= 6; + codePoint |= *src & LOW_6BITS; + } + + if (codePoint >= SURROGATE_PAIR_START) + { + // In UTF-16 U+10000 to U+10FFFF are represented as two 16-bit code units, surrogate pairs. + // - 0x10000 is subtracted from the code point + // - high surrogate is 0xD800 added to the top ten bits + // - low surrogate is 0xDC00 added to the low ten bits + codePoint -= SURROGATE_PAIR_START; + dest.push_back(utf16string::value_type((codePoint >> 10) | H_SURROGATE_START)); + dest.push_back(utf16string::value_type((codePoint & 0x3FF) | L_SURROGATE_START)); + } + else + { + // In UTF-16 U+0000 to U+D7FF and U+E000 to U+FFFF are represented exactly as the Unicode code point value. + // U+D800 to U+DFFF are not valid characters, for simplicity we assume they are not present but will encode + // them if encountered. + dest.push_back(utf16string::value_type(codePoint)); + } + } + } + return dest; +#endif +} + +std::string __cdecl conversions::utf16_to_utf8(const utf16string &w) +{ + #if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) + std::wstring_convert, utf16char> conversion; + return conversion.to_bytes(w); + #else + std::string dest; + dest.reserve(w.size()); + for (auto src = w.begin(); src != w.end(); ++src) + { + // Check for high surrogate. + if (*src >= H_SURROGATE_START && *src <= H_SURROGATE_END) + { + const auto highSurrogate = *src++; + if (src == w.end()) + { + throw std::range_error("UTF-16 string is missing low surrogate"); + } + const auto lowSurrogate = *src; + if (lowSurrogate < L_SURROGATE_START || lowSurrogate > L_SURROGATE_END) + { + throw std::range_error("UTF-16 string has invalid low surrogate"); + } + + // To get from surrogate pair to Unicode code point: + // - subract 0xD800 from high surrogate, this forms top ten bits + // - subract 0xDC00 from low surrogate, this forms low ten bits + // - add 0x10000 + // Leaves a code point in U+10000 to U+10FFFF range. + uint32_t codePoint = highSurrogate - H_SURROGATE_START; + codePoint <<= 10; + codePoint |= lowSurrogate - L_SURROGATE_START; + codePoint += SURROGATE_PAIR_START; + + // 4 bytes need using 21 bits + dest.push_back(char((codePoint >> 18) | 0xF0)); // leading 3 bits + dest.push_back(char(((codePoint >> 12) & LOW_6BITS) | BIT8)); // next 6 bits + dest.push_back(char(((codePoint >> 6) & LOW_6BITS) | BIT8)); // next 6 bits + dest.push_back(char((codePoint & LOW_6BITS) | BIT8)); // trailing 6 bits + } + else + { + if (*src <= 0x7F) // single byte character + { + dest.push_back(static_cast(*src)); + } + else if (*src <= 0x7FF) // 2 bytes needed (11 bits used) + { + dest.push_back(char((*src >> 6) | 0xC0)); // leading 5 bits + dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits + } + else // 3 bytes needed (16 bits used) + { + dest.push_back(char((*src >> 12) | 0xE0)); // leading 4 bits + dest.push_back(char(((*src >> 6) & LOW_6BITS) | BIT8)); // middle 6 bits + dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits + } + } + } + + return dest; + #endif +} + +utf16string __cdecl conversions::usascii_to_utf16(const std::string &s) +{ + // Ascii is a subset of UTF-8 so just convert to UTF-16 + return utf8_to_utf16(s); +} + +utf16string __cdecl conversions::latin1_to_utf16(const std::string &s) +{ + // Latin1 is the first 256 code points in Unicode. + // In UTF-16 encoding each of these is represented as exactly the numeric code point. + utf16string dest; + dest.resize(s.size()); + for (size_t i = 0; i < s.size(); ++i) + { + dest[i] = utf16char(s[i]); + } + return dest; +} + +utf8string __cdecl conversions::latin1_to_utf8(const std::string &s) +{ + return utf16_to_utf8(latin1_to_utf16(s)); +} + +utility::string_t __cdecl conversions::to_string_t(utf16string &&s) +{ +#ifdef _UTF16_STRINGS + return std::move(s); +#else + return utf16_to_utf8(std::move(s)); +#endif +} + +utility::string_t __cdecl conversions::to_string_t(std::string &&s) +{ +#ifdef _UTF16_STRINGS + return utf8_to_utf16(std::move(s)); +#else + return std::move(s); +#endif +} + +utility::string_t __cdecl conversions::to_string_t(const utf16string &s) +{ +#ifdef _UTF16_STRINGS + return s; +#else + return utf16_to_utf8(s); +#endif +} + +utility::string_t __cdecl conversions::to_string_t(const std::string &s) +{ +#ifdef _UTF16_STRINGS + return utf8_to_utf16(s); +#else + return s; +#endif +} + +std::string __cdecl conversions::to_utf8string(std::string value) { return std::move(value); } + +std::string __cdecl conversions::to_utf8string(const utf16string &value) { return utf16_to_utf8(value); } + +utf16string __cdecl conversions::to_utf16string(const std::string &value) { return utf8_to_utf16(value); } + +utf16string __cdecl conversions::to_utf16string(utf16string value) { return std::move(value); } + +static bool is_digit(utility::char_t c) { return c >= _XPLATSTR('0') && c <= _XPLATSTR('9'); } + +} diff --git a/src/corehost/cli/libhost.cpp b/src/corehost/cli/libhost.cpp new file mode 100644 index 000000000..56d030505 --- /dev/null +++ b/src/corehost/cli/libhost.cpp @@ -0,0 +1,61 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "pal.h" +#include "utils.h" +#include "trace.h" +#include "libhost.h" + +pal::string_t get_runtime_config_json(const pal::string_t& app_path) +{ + auto name = get_filename_without_ext(app_path); + auto json_name = name + _X(".runtimeconfig.json"); + auto json_path = get_directory(app_path); + + append_path(&json_path, json_name.c_str()); + if (pal::file_exists(json_path)) + { + return json_path; + } + return pal::string_t(); +} + +host_mode_t detect_operating_mode(const int argc, const pal::char_t* argv[], pal::string_t* p_own_dir) +{ + pal::string_t own_path; + if (!pal::get_own_executable_path(&own_path) || !pal::realpath(&own_path)) + { + trace::error(_X("Failed to locate current executable")); + return host_mode_t::invalid; + } + + pal::string_t own_name = get_filename(own_path); + pal::string_t own_dir = get_directory(own_path); + if (p_own_dir) + { + p_own_dir->assign(own_dir); + } + + pal::string_t own_dll_filename = strip_file_ext(own_name) + _X(".dll"); + pal::string_t own_dll = own_dir; + append_path(&own_dll, own_dll_filename.c_str()); + trace::info(_X("Exists %s"), own_dll.c_str()); + if (coreclr_exists_in_dir(own_dir) || pal::file_exists(own_dll)) + { + pal::string_t own_deps_json = own_dir; + pal::string_t own_deps_filename = strip_file_ext(own_name) + _X(".deps.json"); + pal::string_t own_config_filename = strip_file_ext(own_name) + _X(".runtimeconfig.json"); + append_path(&own_deps_json, own_deps_filename.c_str()); + if (trace::is_enabled()) + { + trace::info(_X("Detecting mode... CoreCLR present in own dir [%s] and checking if [%s] file present=[%d]"), + own_dir.c_str(), own_deps_filename.c_str(), pal::file_exists(own_deps_json)); + } + return ((pal::file_exists(own_deps_json) || !pal::file_exists(own_config_filename)) && pal::file_exists(own_dll)) ? host_mode_t::standalone : host_mode_t::split_fx; + } + else + { + return host_mode_t::muxer; + } +} + diff --git a/src/corehost/cli/libhost.h b/src/corehost/cli/libhost.h index 0b8ea823a..fa0d60e9c 100644 --- a/src/corehost/cli/libhost.h +++ b/src/corehost/cli/libhost.h @@ -1,4 +1,70 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#ifndef __LIBHOST_H__ +#define __LIBHOST_H__ + #define LIBHOST_NAME MAKE_LIBNAME("hostpolicy") + +enum host_mode_t +{ + invalid = 0, + muxer, + standalone, + split_fx +}; + +class runtime_config_t; + +class corehost_init_t +{ + const pal::string_t m_probe_path; + const pal::string_t m_deps_file; + const pal::string_t m_fx_dir; + host_mode_t m_host_mode; + const runtime_config_t* m_runtime_config; +public: + corehost_init_t( + const pal::string_t& deps_file, + const pal::string_t& probe_path, + const pal::string_t& fx_dir, + const host_mode_t mode, + const runtime_config_t* runtime_config) + : m_fx_dir(fx_dir) + , m_runtime_config(runtime_config) + , m_deps_file(deps_file) + , m_probe_path(probe_path) + , m_host_mode(mode) + { + } + + const host_mode_t host_mode() const + { + return m_host_mode; + } + + const pal::string_t& deps_file() const + { + return m_deps_file; + } + + const pal::string_t& probe_dir() const + { + return m_probe_path; + } + + const pal::string_t& fx_dir() const + { + return m_fx_dir; + } + + const runtime_config_t* runtime_config() const + { + return m_runtime_config; + } +}; + +pal::string_t get_runtime_config_json(const pal::string_t& app_path); +host_mode_t detect_operating_mode(const int argc, const pal::char_t* argv[], pal::string_t* own_dir = nullptr); + +#endif // __LIBHOST_H__ diff --git a/src/corehost/cli/runtime_config.cpp b/src/corehost/cli/runtime_config.cpp new file mode 100644 index 000000000..e27c5a3a0 --- /dev/null +++ b/src/corehost/cli/runtime_config.cpp @@ -0,0 +1,114 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "pal.h" +#include "utils.h" +#include "cpprest/json.h" +#include "runtime_config.h" +#include + +typedef web::json::value json_value; + +runtime_config_t::runtime_config_t(const pal::string_t& path) + : m_fx_roll_fwd(true) + , m_path(path) + , m_portable(false) + , m_gc_server(_X("0")) +{ + m_valid = ensure_parsed(); +} + +void parse_fx(const json_value& opts, pal::string_t* name, pal::string_t* version, bool* roll_fwd, bool* portable) +{ + name->clear(); + version->clear(); + *roll_fwd = true; + *portable = false; + + if (opts.is_null()) + { + return; + } + + const auto& opts_obj = opts.as_object(); + auto framework = opts_obj.find(_X("framework")); + if (framework == opts_obj.end()) + { + return; + } + + *portable = true; + + const auto& fx_obj = framework->second.as_object(); + *name = fx_obj.at(_X("name")).as_string(); + *version = fx_obj.at(_X("version")).as_string(); + + auto value = fx_obj.find(_X("rollForward")); + if (value == fx_obj.end()) + { + return; + } + + *roll_fwd = value->second.as_bool(); +} + +bool runtime_config_t::ensure_parsed() +{ + pal::string_t retval; + if (!pal::file_exists(m_path)) + { + // Not existing is not an error. + return true; + } + + pal::ifstream_t file(m_path); + if (!file.good()) + { + return false; + } + + try + { + const auto root = json_value::parse(file); + const auto& json = root.as_object(); + const auto iter = json.find(_X("runtimeOptions")); + if (iter != json.end()) + { + parse_fx(iter->second, &m_fx_name, &m_fx_ver, &m_fx_roll_fwd, &m_portable); + } + } + catch (...) + { + return false; + } + return true; +} + +const pal::string_t& runtime_config_t::get_gc_server() const +{ + assert(m_valid); + return m_gc_server; +} + +const pal::string_t& runtime_config_t::get_fx_name() const +{ + assert(m_valid); + return m_fx_name; +} + +const pal::string_t& runtime_config_t::get_fx_version() const +{ + assert(m_valid); + return m_fx_ver; +} + +bool runtime_config_t::get_fx_roll_fwd() const +{ + assert(m_valid); + return m_fx_roll_fwd; +} + +bool runtime_config_t::get_portable() const +{ + return m_portable; +} diff --git a/src/corehost/cli/runtime_config.h b/src/corehost/cli/runtime_config.h new file mode 100644 index 000000000..bdb14fda1 --- /dev/null +++ b/src/corehost/cli/runtime_config.h @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "pal.h" + +class runtime_config_t +{ +public: + runtime_config_t(const pal::string_t& path); + bool is_valid() { return m_valid; } + const pal::string_t& get_path() { return m_path; } + const pal::string_t& get_gc_server() const; + const pal::string_t& get_fx_version() const; + const pal::string_t& get_fx_name() const; + bool get_fx_roll_fwd() const; + bool get_portable() const; + +private: + bool ensure_parsed(); + + pal::string_t m_gc_server; + pal::string_t m_fx_name; + pal::string_t m_fx_ver; + bool m_fx_roll_fwd; + + pal::string_t m_path; + bool m_portable; + bool m_valid; +}; diff --git a/src/corehost/cli/servicing_index.cpp b/src/corehost/cli/servicing_index.cpp index 687006ff4..8dd9ebd76 100644 --- a/src/corehost/cli/servicing_index.cpp +++ b/src/corehost/cli/servicing_index.cpp @@ -38,18 +38,33 @@ bool servicing_index_t::find_redirection( auto iter = m_redirections.find(stream.str()); if (iter != m_redirections.end()) { - pal::string_t full_path = m_patch_root; - append_path(&full_path, iter->second.c_str()); - if (pal::file_exists(full_path)) + pal::string_t ni_root = m_patch_root; + append_path(&ni_root, get_arch()); + + // First prefer the architecture specific NI image. + pal::string_t paths[2] = { ni_root, m_patch_root }; + for (pal::string_t& full_path : paths) { - *redirection = full_path; - trace::verbose(_X("Servicing %s with %s"), stream.str().c_str(), redirection->c_str()); - return true; + append_path(&full_path, iter->second.c_str()); + if (pal::file_exists(full_path)) + { + *redirection = full_path; + if (trace::is_enabled()) + { + pal::string_t stream_str = stream.str(); + trace::verbose(_X("Servicing %s with %s"), stream_str.c_str(), redirection->c_str()); + } + return true; + } + trace::verbose(_X("Serviced file %s doesn't exist"), full_path.c_str()); } - trace::verbose(_X("Serviced file %s doesn't exist"), full_path.c_str()); } - trace::verbose(_X("Entry %s not serviced or file doesn't exist"), stream.str().c_str()); + if (trace::is_enabled()) + { + auto stream_str = stream.str(); + trace::verbose(_X("Entry %s not serviced or file doesn't exist"), stream_str.c_str()); + } return false; } @@ -112,7 +127,8 @@ void servicing_index_t::ensure_redirections() if (trace::is_enabled()) { - trace::verbose(_X("Adding servicing entry %s => %s"), sstream.str().c_str(), str.substr(from).c_str()); + auto stream_str = sstream.str(); + trace::verbose(_X("Adding servicing entry %s => %s"), stream_str.c_str(), str.substr(from).c_str()); } // Store just the filename. diff --git a/src/corehost/cli/setup.cmake b/src/corehost/cli/setup.cmake index ef2028035..d3e65f55d 100644 --- a/src/corehost/cli/setup.cmake +++ b/src/corehost/cli/setup.cmake @@ -33,4 +33,20 @@ if(WIN32) set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /DEBUG /OPT:REF /OPT:ICF") set(CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO} /DEBUG /OPT:REF /OPT:ICF") set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /DEBUG /OPT:REF /OPT:ICF") +else() + add_compile_options(-Wno-unused-local-typedef) +endif() + +if(CLI_CMAKE_PLATFORM_ARCH_I386) + add_definitions(-D_TARGET_X86_=1) +elseif(CLI_CMAKE_PLATFORM_ARCH_AMD64) + add_definitions(-D_TARGET_AMD64_=1) +else() + message(FATAL_ERROR "Unknown target architecture") +endif() + +if(${CLI_CMAKE_RUNTIME_ID} STREQUAL "") + message(FATAL_ERROR "Runtime ID not specified") +else() + add_definitions(-DTARGET_RUNTIME_ID="${CLI_CMAKE_RUNTIME_ID}") endif() diff --git a/src/corehost/common/pal.h b/src/corehost/common/pal.h index 930104d34..dfd76b3a6 100644 --- a/src/corehost/common/pal.h +++ b/src/corehost/common/pal.h @@ -15,10 +15,12 @@ #include #include #include +#include #if defined(_WIN32) -#include +#define NOMINMAX +#include #define HOST_EXE_NAME L"corehost.exe" #define xerr std::wcerr @@ -93,11 +95,17 @@ namespace pal typedef HMODULE dll_t; typedef FARPROC proc_t; + pal::string_t to_string(int value); + + bool getcwd(pal::string_t* recv); + inline int strcmp(const char_t* str1, const char_t* str2) { return ::wcscmp(str1, str2); } inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::_wcsicmp(str1, str2); } inline int strncmp(const char_t* str1, const char_t* str2, int len) { return ::wcsncmp(str1, str2, len); } inline int strncasecmp(const char_t* str1, const char_t* str2, int len) { return ::_wcsnicmp(str1, str2, len); } + pal::string_t to_lower(const pal::string_t& in); + inline size_t strlen(const char_t* str) { return ::wcslen(str); } inline void err_vprintf(const char_t* format, va_list vl) { ::vfwprintf(stderr, format, vl); ::fputws(_X("\r\n"), stderr); } @@ -126,10 +134,17 @@ namespace pal typedef void* dll_t; typedef void* proc_t; + pal::string_t to_string(int value); + + bool getcwd(pal::string_t* recv); + inline int strcmp(const char_t* str1, const char_t* str2) { return ::strcmp(str1, str2); } inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::strcasecmp(str1, str2); } inline int strncmp(const char_t* str1, const char_t* str2, int len) { return ::strncmp(str1, str2, len); } inline int strncasecmp(const char_t* str1, const char_t* str2, int len) { return ::strncasecmp(str1, str2, len); } + + pal::string_t to_lower(const pal::string_t& in); + inline size_t strlen(const char_t* str) { return ::strlen(str); } inline void err_vprintf(const char_t* format, va_list vl) { ::vfprintf(stderr, format, vl); ::fputc('\n', stderr); } inline pal::string_t to_palstring(const std::string& str) { return str; } diff --git a/src/corehost/common/pal.unix.cpp b/src/corehost/common/pal.unix.cpp index ead8034d8..a58ad8761 100644 --- a/src/corehost/common/pal.unix.cpp +++ b/src/corehost/common/pal.unix.cpp @@ -10,6 +10,8 @@ #include #include +#include + #if defined(__APPLE__) #include #endif @@ -20,6 +22,33 @@ #define symlinkEntrypointExecutable "/proc/curproc/exe" #endif +pal::string_t pal::to_string(int value) { return std::to_string(value); } + +pal::string_t pal::to_lower(const pal::string_t& in) +{ + pal::string_t ret = in; + std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); + return ret; +} + +bool pal::getcwd(pal::string_t* recv) +{ + recv->clear(); + pal::char_t* buf = ::getcwd(nullptr, PATH_MAX + 1); + if (buf == nullptr) + { + if (errno == ENOENT) + { + return false; + } + perror("getcwd()"); + return false; + } + recv->assign(buf); + ::free(buf); + return true; +} + bool pal::find_coreclr(pal::string_t* recv) { pal::string_t candidate; @@ -133,8 +162,7 @@ bool pal::getenv(const pal::char_t* name, pal::string_t* recv) bool pal::realpath(pal::string_t* path) { - pal::char_t buf[PATH_MAX]; - auto resolved = ::realpath(path->c_str(), buf); + auto resolved = ::realpath(path->c_str(), nullptr); if (resolved == nullptr) { if (errno == ENOENT) @@ -145,6 +173,7 @@ bool pal::realpath(pal::string_t* path) return false; } path->assign(resolved); + ::free(resolved); return true; } diff --git a/src/corehost/common/pal.windows.cpp b/src/corehost/common/pal.windows.cpp index d70bd60e9..a5ce39c7f 100644 --- a/src/corehost/common/pal.windows.cpp +++ b/src/corehost/common/pal.windows.cpp @@ -11,6 +11,18 @@ static std::wstring_convert, wchar_t> g_converter; +pal::string_t pal::to_lower(const pal::string_t& in) +{ + pal::string_t ret = in; + std::transform(ret.begin(), ret.end(), ret.begin(), ::towlower); + return ret; +} + +pal::string_t pal::to_string(int value) +{ + return std::to_wstring(value); +} + bool pal::find_coreclr(pal::string_t* recv) { pal::string_t candidate; @@ -31,6 +43,35 @@ bool pal::find_coreclr(pal::string_t* recv) return false; } + +bool pal::getcwd(pal::string_t* recv) +{ + recv->clear(); + + pal::char_t buf[MAX_PATH]; + DWORD result = GetCurrentDirectoryW(MAX_PATH, buf); + if (result < MAX_PATH) + { + recv->assign(buf); + return true; + } + else if (result != 0) + { + std::vector str; + str.resize(result); + result = GetCurrentDirectoryW(str.size(), str.data()); + assert(result <= str.size()); + if (result != 0) + { + recv->assign(str.data()); + return true; + } + } + assert(result == 0); + trace::error(_X("Failed to obtain working directory, HRESULT: 0x%X"), HRESULT_FROM_WIN32(GetLastError())); + return false; +} + bool pal::load_library(const char_t* path, dll_t* dll) { *dll = ::LoadLibraryW(path); diff --git a/src/corehost/common/utils.cpp b/src/corehost/common/utils.cpp index 2515b2155..65f1b8c42 100644 --- a/src/corehost/common/utils.cpp +++ b/src/corehost/common/utils.cpp @@ -55,16 +55,43 @@ pal::string_t get_executable(const pal::string_t& filename) return result; } -pal::string_t get_filename(const pal::string_t& path) +pal::string_t strip_file_ext(const pal::string_t& path) { - // Find the last dir separator - auto path_sep = path.find_last_of(DIR_SEPARATOR); - if (path_sep == pal::string_t::npos) + if (path.empty()) { - return pal::string_t(path); + return path; + } + return path.substr(0, path.rfind(_X('.'))); +} + +pal::string_t get_filename_without_ext(const pal::string_t& path) +{ + if (path.empty()) + { + return path; } - return path.substr(path_sep + 1); + size_t name_pos = path.find_last_of(_X("/\\")); + size_t dot_pos = path.rfind(_X('.')); + size_t start_pos = (name_pos == pal::string_t::npos) ? 0 : (name_pos + 1); + size_t count = (dot_pos == pal::string_t::npos) ? pal::string_t::npos : (dot_pos - start_pos); + return path.substr(start_pos, count); +} + +pal::string_t get_filename(const pal::string_t& path) +{ + if (path.empty()) + { + return path; + } + + auto name_pos = path.find_last_of(DIR_SEPARATOR); + if (name_pos == pal::string_t::npos) + { + return path; + } + + return path.substr(name_pos + 1); } pal::string_t get_directory(const pal::string_t& path) @@ -87,3 +114,49 @@ void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl) (*path)[pos] = repl; } } + +const pal::char_t* get_arch() +{ +#if _TARGET_AMD64_ + return _X("x64"); +#elif _TARGET_X86_ + return _X("x86"); +#else +#error "Unknown target" +#endif +} + +bool parse_known_args( + const int argc, + const pal::char_t* argv[], + const std::vector& known_opts, + std::unordered_map* opts, + int* num_args) +{ + int arg_i = *num_args; + while (arg_i < argc) + { + pal::string_t arg = argv[arg_i]; + if (std::find(known_opts.begin(), known_opts.end(), pal::to_lower(arg)) == known_opts.end()) + { + // Unknown argument. + break; + } + + // Known argument, so expect one more arg (value) to be present. + if (arg_i + 1 >= argc) + { + return false; + } + + (*opts)[arg] = argv[arg_i + 1]; + + // Increment for both the option and its value. + arg_i += 2; + } + + *num_args = arg_i; + + return true; +} + diff --git a/src/corehost/common/utils.h b/src/corehost/common/utils.h index 9d2dc2098..bbcdae4ad 100644 --- a/src/corehost/common/utils.h +++ b/src/corehost/common/utils.h @@ -10,8 +10,17 @@ bool ends_with(const pal::string_t& value, const pal::string_t& suffix, bool mat bool starts_with(const pal::string_t& value, const pal::string_t& prefix, bool match_case); pal::string_t get_executable(const pal::string_t& filename); pal::string_t get_directory(const pal::string_t& path); +pal::string_t strip_file_ext(const pal::string_t& path); pal::string_t get_filename(const pal::string_t& path); +pal::string_t get_filename_without_ext(const pal::string_t& path); void append_path(pal::string_t* path1, const pal::char_t* path2); bool coreclr_exists_in_dir(const pal::string_t& candidate); void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl); +const pal::char_t* get_arch(); +bool parse_known_args( + const int argc, + const pal::char_t* argv[], + const std::vector& known_opts, + std::unordered_map* opts, + int* num_args); #endif diff --git a/src/corehost/corehost.cpp b/src/corehost/corehost.cpp index 70e4d7d36..bf1904faa 100644 --- a/src/corehost/corehost.cpp +++ b/src/corehost/corehost.cpp @@ -2,64 +2,140 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "trace.h" -#include "pal.h" #include "utils.h" -#include "libhost.h" +#include "corehost.h" +#include "fx_ver.h" +#include "error_codes.h" +#include "policy_load.h" +#include -extern int corehost_main(const int argc, const pal::char_t* argv[]); +#define LIBFXR_NAME MAKE_LIBNAME("hostfxr") -namespace +bool corehost_t::hostpolicy_exists_in_svc(pal::string_t* resolved_dir) { -enum StatusCode -{ - Success = 0, - CoreHostLibLoadFailure = 0x41, - CoreHostLibMissingFailure = 0x42, - CoreHostEntryPointFailure = 0x43, - CoreHostCurExeFindFailure = 0x44, -}; - -typedef int (*corehost_main_fn) (const int argc, const pal::char_t* argv[]); - -// ----------------------------------------------------------------------------- -// Load the corehost library from the path specified -// -// Parameters: -// lib_dir - dir path to the corehost library -// h_host - handle to the library which will be kept live -// main_fn - Contains the entrypoint "corehost_main" when returns success. -// -// Returns: -// Non-zero exit code on failure. "main_fn" contains "corehost_main" -// entrypoint on success. -// -StatusCode load_host_lib(const pal::string_t& lib_dir, pal::dll_t* h_host, corehost_main_fn* main_fn) -{ - pal::string_t host_path = lib_dir; - append_path(&host_path, LIBHOST_NAME); - - // Missing library - if (!pal::file_exists(host_path)) +#ifdef COREHOST_PACKAGE_SERVICING + pal::string_t svc_dir; + if (!pal::getenv(_X("DOTNET_SERVICING"), &svc_dir)) { - return StatusCode::CoreHostLibMissingFailure; + return false; } - // Load library - if (!pal::load_library(host_path.c_str(), h_host)) + pal::string_t path = svc_dir; + append_path(&path, COREHOST_PACKAGE_NAME); + append_path(&path, COREHOST_PACKAGE_VERSION); + append_path(&path, COREHOST_PACKAGE_COREHOST_RELATIVE_DIR); + if (library_exists_in_dir(path, LIBHOST_NAME)) { - trace::info(_X("Load library of %s failed"), host_path.c_str()); + resolved_dir->assign(path); + } + return true; +#else + return false; +#endif +} + +pal::string_t corehost_t::resolve_fxr_path(const pal::string_t& own_dir) +{ + pal::string_t fxr_path; + + pal::string_t fxr_dir = own_dir; + append_path(&fxr_dir, _X("dotnethost")); + append_path(&fxr_dir, _X("fxr")); + if (pal::directory_exists(fxr_dir)) + { + trace::info(_X("Reading fx resolver directory=[%s]"), fxr_dir.c_str()); + + std::vector list; + pal::readdir(fxr_dir, &list); + + fx_ver_t max_ver(-1, -1, -1); + for (const auto& dir : list) + { + trace::info(_X("Considering fxr version=[%s]..."), dir.c_str()); + + pal::string_t ver = get_filename(dir); + + fx_ver_t fx_ver(-1, -1, -1); + if (fx_ver_t::parse(ver, &fx_ver, false)) + { + max_ver = std::max(max_ver, fx_ver); + } + } + + pal::string_t max_ver_str = max_ver.as_str(); + append_path(&fxr_dir, max_ver_str.c_str()); + trace::info(_X("Detected latest fxr version=[%s]..."), fxr_dir.c_str()); + } + + const pal::string_t* dirs[] = { &fxr_dir, &own_dir }; + for (const auto& dir : dirs) + { + trace::info(_X("Considering fxr dir=[%s]..."), fxr_dir.c_str()); + if (policy_load_t::library_exists_in_dir(*dir, LIBFXR_NAME, &fxr_path)) + { + trace::info(_X("Resolved fxr [%s]..."), fxr_path.c_str()); + return fxr_path; + } + } + return pal::string_t(); +} + +int corehost_t::resolve_fx_and_execute_app(const pal::string_t& own_dir, const int argc, const pal::char_t* argv[]) +{ + pal::dll_t fxr; + + pal::string_t fxr_path = resolve_fxr_path(own_dir); + + // Load library + if (!pal::load_library(fxr_path.c_str(), &fxr)) + { + trace::info(_X("Load library of %s failed"), fxr_path.c_str()); return StatusCode::CoreHostLibLoadFailure; } - // Obtain entrypoint symbol - *main_fn = (corehost_main_fn) pal::get_symbol(*h_host, "corehost_main"); - - return (*main_fn != nullptr) - ? StatusCode::Success - : StatusCode::CoreHostEntryPointFailure; + // Obtain entrypoint symbols + hostfxr_main_fn main_fn = (hostfxr_main_fn) pal::get_symbol(fxr, "hostfxr_main"); + return main_fn(argc, argv); } -}; // end of anonymous namespace +int corehost_t::run(const int argc, const pal::char_t* argv[]) +{ + pal::string_t own_dir; + auto mode = detect_operating_mode(argc, argv, &own_dir); + + switch (mode) + { + case muxer: + trace::info(_X("Host operating in Muxer mode")); + return resolve_fx_and_execute_app(own_dir, argc, argv); + + case split_fx: + { + trace::info(_X("Host operating in split mode; own dir=[%s]"), own_dir.c_str()); + corehost_init_t init(_X(""), _X(""), own_dir, host_mode_t::split_fx, nullptr); + return policy_load_t::execute_app(own_dir, &init, argc, argv); + } + + case standalone: + { + trace::info(_X("Host operating from standalone app dir %s"), own_dir.c_str()); + + pal::string_t svc_dir; + corehost_init_t init(_X(""), _X(""), _X(""), host_mode_t::standalone, nullptr); + return policy_load_t::execute_app( + hostpolicy_exists_in_svc(&svc_dir) ? svc_dir : own_dir, &init, argc, argv); + } + return StatusCode::CoreHostLibMissingFailure; + + default: + trace::error(_X("Unknown mode detected or could not resolve the mode.")); + return StatusCode::CoreHostResolveModeFailure; + } +} + +#include + +#include "deps_format.h" #if defined(_WIN32) int __cdecl wmain(const int argc, const pal::char_t* argv[]) @@ -69,56 +145,19 @@ int main(const int argc, const pal::char_t* argv[]) { trace::setup(); - pal::dll_t corehost; + //deps_json_t deps(true, _X("H:\\code\\sharedfx\\PortableApp\\PortableAppWithNative.deps.json")); + //deps_json_t deps2(false, _X("H:\\code\\sharedfx\\StandaloneApp\\StandaloneApp.deps.json")); -#ifdef COREHOST_PACKAGE_SERVICING - // No custom host asked, so load the corehost if serviced first. - pal::string_t svc_dir; - if (pal::getenv(_X("DOTNET_SERVICING"), &svc_dir)) + if (trace::is_enabled()) { - pal::string_t path = svc_dir; - append_path(&path, COREHOST_PACKAGE_NAME); - append_path(&path, COREHOST_PACKAGE_VERSION); - append_path(&path, COREHOST_PACKAGE_COREHOST_RELATIVE_DIR); - - corehost_main_fn host_main; - StatusCode code = load_host_lib(path, &corehost, &host_main); - if (code != StatusCode::Success) + trace::info(_X("--- Invoked host main = {")); + for (int i = 0; i < argc; ++i) { - trace::info(_X("Failed to load host library from servicing dir: %s; Status=%08X"), path.c_str(), code); - // Ignore all errors for the servicing case, and proceed to the next step. - } - else - { - trace::info(_X("Calling host entrypoint from library at servicing dir %s"), path.c_str()); - return host_main(argc, argv); + trace::info(_X("%s"), argv[i]); } + trace::info(_X("}")); } -#endif - - // Get current path to look for the library app locally. - pal::string_t own_path; - if (!pal::get_own_executable_path(&own_path) || !pal::realpath(&own_path)) - { - trace::error(_X("Failed to locate current executable")); - return StatusCode::CoreHostCurExeFindFailure; - } - - // Local load of the corehost library. - auto own_dir = get_directory(own_path); - - corehost_main_fn host_main; - StatusCode code = load_host_lib(own_dir, &corehost, &host_main); - switch (code) - { - // Success, call the entrypoint. - case StatusCode::Success: - trace::info(_X("Calling host entrypoint from library at own dir %s"), own_dir.c_str()); - return host_main(argc, argv); - - // Some other fatal error including StatusCode::CoreHostLibMissingFailure. - default: - trace::error(_X("Error loading the host library from own dir: %s; Status=%08X"), own_dir.c_str(), code); - return code; - } + corehost_t corehost; + return corehost.run(argc, argv); } + diff --git a/src/corehost/corehost.h b/src/corehost/corehost.h new file mode 100644 index 000000000..92063e673 --- /dev/null +++ b/src/corehost/corehost.h @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "pal.h" +#include "libhost.h" +#include "policy_load.h" + +typedef int(*hostfxr_main_fn) (const int argc, const pal::char_t* argv[]); + +class corehost_t +{ +public: + + int run(const int argc, const pal::char_t* argv[]); + static int execute_app( + const pal::string_t& policy_dir, + const pal::string_t& fx_dir, + const runtime_config_t* config, + const int argc, + const pal::char_t* argv[]); + +private: + + static int load_host_library( + const pal::string_t& lib_dir, + pal::dll_t* h_host, + corehost_load_fn* load_fn, + corehost_main_fn* main_fn, + corehost_unload_fn* unload_fn); + + pal::string_t resolve_fxr_path(const pal::string_t& own_dir); + int resolve_fx_and_execute_app(const pal::string_t& own_dir, const int argc, const pal::char_t* argv[]); + + static bool hostpolicy_exists_in_svc(pal::string_t* resolved_path); + static bool library_exists_in_dir(const pal::string_t& lib_dir, const pal::string_t& lib_name, pal::string_t* p_host_path); +}; diff --git a/src/corehost/error_codes.h b/src/corehost/error_codes.h new file mode 100644 index 000000000..d5a0bc141 --- /dev/null +++ b/src/corehost/error_codes.h @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef __ERROR_CODES_H__ +#define __ERROR_CODES_H__ +enum StatusCode +{ + Success = 0, + InvalidArgFailure = 0x81, + CoreHostLibLoadFailure = 0x82, + CoreHostLibMissingFailure = 0x83, + CoreHostEntryPointFailure = 0x84, + CoreHostCurExeFindFailure = 0x85, + CoreHostResolveModeFailure = 0x86, + CoreClrResolveFailure = 0x87, + CoreClrBindFailure = 0x88, + CoreClrInitFailure = 0x89, + CoreClrExeFailure = 0x90, + ResolverInitFailure = 0x91, + ResolverResolveFailure = 0x92, + LibHostCurExeFindFailure = 0x93, + LibHostInitFailure = 0x94, + LibHostMuxFailure = 0x95, + LibHostExecModeFailure = 0x96, + LibHostSdkFindFailure = 0x97, + LibHostInvalidArgs = 0x98, + InvalidConfigFile = 0x99, +}; +#endif // __ERROR_CODES_H__ diff --git a/src/corehost/policy_load.cpp b/src/corehost/policy_load.cpp new file mode 100644 index 000000000..514f8a086 --- /dev/null +++ b/src/corehost/policy_load.cpp @@ -0,0 +1,79 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "policy_load.h" +#include "corehost.h" + +bool policy_load_t::library_exists_in_dir(const pal::string_t& lib_dir, const pal::string_t& lib_name, pal::string_t* p_host_path) +{ + pal::string_t host_path = lib_dir; + append_path(&host_path, lib_name.c_str()); + + if (!pal::file_exists(host_path)) + { + return false; + } + if (p_host_path) + { + *p_host_path = host_path; + } + return true; +} + +int policy_load_t::load_host_library( + const pal::string_t& lib_dir, + pal::dll_t* h_host, + corehost_load_fn* load_fn, + corehost_main_fn* main_fn, + corehost_unload_fn* unload_fn) +{ + pal::string_t host_path; + if (!library_exists_in_dir(lib_dir, LIBHOST_NAME, &host_path)) + { + return StatusCode::CoreHostLibMissingFailure; + } + + // Load library + if (!pal::load_library(host_path.c_str(), h_host)) + { + trace::info(_X("Load library of %s failed"), host_path.c_str()); + return StatusCode::CoreHostLibLoadFailure; + } + + // Obtain entrypoint symbols + *load_fn = (corehost_load_fn)pal::get_symbol(*h_host, "corehost_load"); + *main_fn = (corehost_main_fn)pal::get_symbol(*h_host, "corehost_main"); + *unload_fn = (corehost_unload_fn)pal::get_symbol(*h_host, "corehost_unload"); + + return (*main_fn) && (*load_fn) && (*unload_fn) + ? StatusCode::Success + : StatusCode::CoreHostEntryPointFailure; +} + +int policy_load_t::execute_app( + const pal::string_t& impl_dll_dir, + const corehost_init_t* init, + const int argc, + const pal::char_t* argv[]) +{ + pal::dll_t corehost; + corehost_main_fn host_main = nullptr; + corehost_load_fn host_load = nullptr; + corehost_unload_fn host_unload = nullptr; + + int code = load_host_library(impl_dll_dir, &corehost, &host_load, &host_main, &host_unload); + + if (code != StatusCode::Success) + { + trace::error(_X("Could not load host policy library [%s]"), impl_dll_dir.c_str()); + return code; + } + + if ((code = host_load(init)) == 0) + { + code = host_main(argc, argv); + (void) host_unload(); + } + return code; +} + diff --git a/src/corehost/policy_load.h b/src/corehost/policy_load.h new file mode 100644 index 000000000..8d3cc7411 --- /dev/null +++ b/src/corehost/policy_load.h @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef __POLICY_LOAD_H__ +#define __POLICY_LOAD_H__ + +#include "pal.h" +#include "utils.h" +#include "trace.h" +#include "error_codes.h" + +class corehost_init_t; +class runtime_config_t; + +typedef int(*corehost_load_fn) (const corehost_init_t* init); +typedef int(*corehost_main_fn) (const int argc, const pal::char_t* argv[]); +typedef int(*corehost_unload_fn) (); + +class policy_load_t +{ +public: + static int execute_app( + const pal::string_t& impl_dll_dir, + const corehost_init_t* init, + const int argc, + const pal::char_t* argv[]); + + static bool library_exists_in_dir(const pal::string_t& lib_dir, const pal::string_t& lib_name, pal::string_t* p_host_path = nullptr); + + static int load_host_library( + const pal::string_t& lib_dir, + pal::dll_t* h_host, + corehost_load_fn* load_fn, + corehost_main_fn* main_fn, + corehost_unload_fn* unload_fn); +}; + +#endif // __POLICY_LOAD_H__ diff --git a/src/dotnet/commands/dotnet-compile-native/appdep/project.json b/src/dotnet/commands/dotnet-compile-native/appdep/project.json index 689ef478a..eaac3555d 100644 --- a/src/dotnet/commands/dotnet-compile-native/appdep/project.json +++ b/src/dotnet/commands/dotnet-compile-native/appdep/project.json @@ -1,15 +1,15 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "Microsoft.DotNet.AppDep":"1.0.6-prerelease-00003" - }, - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.AppDep": "1.0.6-prerelease-00003" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/src/dotnet/commands/dotnet-new/CSharp_Console/project.json.template b/src/dotnet/commands/dotnet-new/CSharp_Console/project.json.template index c02d0dffe..2e5ebbe30 100644 --- a/src/dotnet/commands/dotnet-new/CSharp_Console/project.json.template +++ b/src/dotnet/commands/dotnet-new/CSharp_Console/project.json.template @@ -1,16 +1,14 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/src/dotnet/commands/dotnet-new/FSharp_Console/project.json.template b/src/dotnet/commands/dotnet-new/FSharp_Console/project.json.template index 60ba0af6e..84d87f6df 100644 --- a/src/dotnet/commands/dotnet-new/FSharp_Console/project.json.template +++ b/src/dotnet/commands/dotnet-new/FSharp_Console/project.json.template @@ -1,22 +1,19 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "compilerName": "fsc", - "compileFiles": [ - "Program.fs" - ], - - "dependencies": { - "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-151221", - "NETStandard.Library": "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "compilerName": "fsc", + "compileFiles": [ + "Program.fs" + ], + "dependencies": { + "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-151221", + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" } + } } diff --git a/src/dotnet/commands/dotnet-restore/Program.cs b/src/dotnet/commands/dotnet-restore/Program.cs index 1b530edb0..806604cf6 100644 --- a/src/dotnet/commands/dotnet-restore/Program.cs +++ b/src/dotnet/commands/dotnet-restore/Program.cs @@ -162,14 +162,21 @@ namespace Microsoft.DotNet.Tools.Restore Path.GetDirectoryName(toolDescription.RuntimeAssemblies.First().Path), toolDescription.Identity.Name + FileNameSuffixes.Deps); + var depsJsonPath = Path.Combine( + toolDescription.Path, + Path.GetDirectoryName(toolDescription.RuntimeAssemblies.First().Path), + toolDescription.Identity.Name + FileNameSuffixes.DepsJson); + var calculator = context.GetOutputPaths(Constants.DefaultConfiguration, buidBasePath: null, outputPath: context.ProjectDirectory); var executable = new Executable(context, calculator, context.CreateExporter(Constants.DefaultConfiguration), null); executable.MakeCompilationOutputRunnable(); if (File.Exists(depsPath)) File.Delete(depsPath); + if (File.Exists(depsJsonPath)) File.Delete(depsJsonPath); File.Move(Path.Combine(calculator.RuntimeOutputPath, "bin" + FileNameSuffixes.Deps), depsPath); + File.Move(Path.Combine(calculator.RuntimeOutputPath, "bin" + FileNameSuffixes.DepsJson), depsJsonPath); } private static bool RestoreToolToPath(LibraryRange tooldep, IEnumerable args, string tempPath, bool quiet) diff --git a/src/dotnet/commands/dotnet-test/dotnet-test.xproj b/src/dotnet/commands/dotnet-test/dotnet-test.xproj index 7f173a43e..fcacb1dd3 100644 --- a/src/dotnet/commands/dotnet-test/dotnet-test.xproj +++ b/src/dotnet/commands/dotnet-test/dotnet-test.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + f003f228-2ae2-4e9d-877b-93eb773b5061 ..\..\artifacts\obj\$(MSBuildProjectName) @@ -13,5 +13,5 @@ 2.0 - + \ No newline at end of file diff --git a/src/dotnet/dotnet.xproj b/src/dotnet/dotnet.xproj index eb07f538d..a8a83293d 100644 --- a/src/dotnet/dotnet.xproj +++ b/src/dotnet/dotnet.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 60cf7e6c-d6c8-439d-b7b7-d8a27e29be2c Microsoft.DotNet.Cli @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/src/dotnet/project.json b/src/dotnet/project.json index b4a5354f1..7938f0157 100644 --- a/src/dotnet/project.json +++ b/src/dotnet/project.json @@ -1,77 +1,72 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "compileExclude": [ + "commands/dotnet-new/CSharp_Console/**", + "commands/dotnet-new/FSharp_Console/**" + ], + "resource": [ + "commands/dotnet-new/CSharp_Console/NuGet.Config", + "commands/dotnet-new/CSharp_Console/Program.cs", + "commands/dotnet-new/CSharp_Console/project.json.template", + "commands/dotnet-new/FSharp_Console/NuGet.config", + "commands/dotnet-new/FSharp_Console/Program.fs", + "commands/dotnet-new/FSharp_Console/project.json.template" + ], + "dependencies": { + "Newtonsoft.Json": "7.0.1", + "Microsoft.Net.Compilers.netcore": "1.3.0-beta1-20160225-02", + "Microsoft.FSharp.Compiler.netcore": "1.0.0-alpha-151218", + "Microsoft.Net.CSharp.Interactive.netcore": "1.3.0-beta1-20160225-02", + "Microsoft.CodeAnalysis.CSharp": "1.3.0-beta1-20160225-02", + "Microsoft.DiaSymReader.Native": "1.3.3", + "NuGet.CommandLine.XPlat": "3.4.0-rtm-0763", + "System.CommandLine": "0.1.0-e160119-1", + "Microsoft.DotNet.ProjectModel": "1.0.0-*", + "Microsoft.DotNet.Compiler.Common": "1.0.0-*", + "Microsoft.DotNet.Cli.Utils": "1.0.0-*", + "Microsoft.DotNet.ILCompiler.SDK": "1.0.6-prerelease-00003", + "Microsoft.Extensions.Logging": "1.0.0-rc2-16040", + "Microsoft.Extensions.Logging.Console": "1.0.0-rc2-16040", + "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537", + "Microsoft.Extensions.CommandLineUtils.Sources": { + "type": "build", + "version": "1.0.0-rc2-16453" }, - "compileExclude": [ - "commands/dotnet-new/CSharp_Console/**", - "commands/dotnet-new/FSharp_Console/**" - ], - "resource": [ - "commands/dotnet-new/CSharp_Console/NuGet.Config", - "commands/dotnet-new/CSharp_Console/Program.cs", - "commands/dotnet-new/CSharp_Console/project.json.template", - "commands/dotnet-new/FSharp_Console/NuGet.config", - "commands/dotnet-new/FSharp_Console/Program.fs", - "commands/dotnet-new/FSharp_Console/project.json.template" - ], - "dependencies": { - "Newtonsoft.Json": "7.0.1", - - "Microsoft.Net.Compilers.netcore": "1.3.0-beta1-20160225-02", - "Microsoft.FSharp.Compiler.netcore": "1.0.0-alpha-151218", - "Microsoft.Net.CSharp.Interactive.netcore": "1.3.0-beta1-20160225-02", - "Microsoft.CodeAnalysis.CSharp": "1.3.0-beta1-20160225-02", - "Microsoft.DiaSymReader.Native": "1.3.3", - - "NuGet.CommandLine.XPlat": "3.4.0-rtm-0733", - "System.CommandLine": "0.1.0-e160119-1", - - "Microsoft.DotNet.ProjectModel": "1.0.0-*", - "Microsoft.DotNet.Compiler.Common": "1.0.0-*", - "Microsoft.DotNet.Cli.Utils": "1.0.0-*", - "Microsoft.DotNet.ILCompiler.SDK": "1.0.6-prerelease-00003", - - "Microsoft.Extensions.Logging": "1.0.0-rc2-16040", - "Microsoft.Extensions.Logging.Console": "1.0.0-rc2-16040", - "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537", - "Microsoft.Extensions.CommandLineUtils.Sources": { - "type": "build", - "version": "1.0.0-rc2-16453" - }, - "Microsoft.Dnx.Runtime.CommandParsing.Sources": { - "version": "1.0.0-rc2-16453", - "type": "build" - }, - "Microsoft.Dnx.Runtime.Sources": { - "version": "1.0.0-rc2-16453", - "type": "build" - }, - "Microsoft.Extensions.Testing.Abstractions": "1.0.0-*", - "Microsoft.NETCore.ConsoleHost": "1.0.0-rc2-23901", - "Microsoft.NETCore.TestHost": "1.0.0-rc2-23901", - "NETStandard.Library": "1.0.0-rc2-23901", - "System.Reflection.Metadata": "1.3.0-beta-23901", - "System.Diagnostics.TextWriterTraceListener": "4.0.0-rc2-23901", - "System.Diagnostics.TraceSource": "4.0.0-rc2-23901", - "System.Linq.Expressions": "4.0.11-rc2-23901", - "System.Xml.XDocument": "4.0.11-rc2-23901", - "System.Resources.ReaderWriter": "4.0.0-rc2-23901", - "System.Net.Sockets": "4.1.0-rc2-23901", - "System.IO.Compression.ZipFile": "4.0.1-rc2-23901", - "System.Threading.ThreadPool": "4.0.10-rc2-23901", - "System.Runtime.Serialization.Primitives": "4.1.0-rc2-23901", - - "System.Private.DataContractSerialization": "4.1.0-rc2-23901" + "Microsoft.Dnx.Runtime.CommandParsing.Sources": { + "version": "1.0.0-rc2-16453", + "type": "build" }, - "frameworks": { - "netstandardapp1.5": { - "imports": [ - "dnxcore50", - "portable-net45+win8" - ] - } + "Microsoft.Dnx.Runtime.Sources": { + "version": "1.0.0-rc2-16453", + "type": "build" }, - "scripts": { + "Microsoft.Extensions.Testing.Abstractions": "1.0.0-*", + "Microsoft.NETCore.ConsoleHost": "1.0.0-rc2-23911", + "Microsoft.NETCore.TestHost": "1.0.0-rc2-23911", + "NETStandard.Library": "1.5.0-rc2-23911", + "System.Reflection.Metadata": "1.3.0-rc2-23911", + "System.Diagnostics.TextWriterTraceListener": "4.0.0-rc2-23911", + "System.Diagnostics.TraceSource": "4.0.0-rc2-23911", + "System.Linq.Expressions": "4.0.11-rc2-23911", + "System.Xml.XDocument": "4.0.11-rc2-23911", + "System.Resources.ReaderWriter": "4.0.0-rc2-23911", + "System.Net.Sockets": "4.1.0-rc2-23911", + "System.Threading.ThreadPool": "4.0.10-rc2-23911", + "Microsoft.Win32.Registry": { + "version": "4.0.0-rc2-23911", + "exclude": "Compile" } + }, + "frameworks": { + "netstandardapp1.5": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ] + } + }, + "scripts": {} } diff --git a/src/sharedframework/rid-fallbacks/windows.json b/src/sharedframework/rid-fallbacks/windows.json new file mode 100644 index 000000000..aff335e39 --- /dev/null +++ b/src/sharedframework/rid-fallbacks/windows.json @@ -0,0 +1,12 @@ +{ + "runtimes": { + "win10-x64": [ "win10", "win81-x64", "win81", "win8-x64", "win8", "win7-x64", "win7", "win-x64", "win", "any", "base" ], + "win10-x86": [ "win10", "win81-x86", "win81", "win8-x86", "win8", "win7-x86", "win7", "win-x86", "win", "any", "base" ], + "win81-x64": [ "win81", "win8-x64", "win8", "win7-x64", "win7", "win-x64", "win", "any", "base" ], + "win81-x86": [ "win81", "win8-x86", "win8", "win7-x86", "win7", "win-x86", "win", "any", "base" ], + "win8-x64": [ "win8", "win7-x64", "win7", "win-x64", "win", "any", "base" ], + "win8-x86": [ "win8", "win7-x86", "win7", "win-x86", "win", "any", "base" ], + "win7-x64": [ "win7", "win-x64", "win", "any", "base" ], + "win7-x86": [ "win7", "win-x86", "win", "any", "base" ] + } +} diff --git a/test/ArgumentForwardingTests/ArgumentForwardingTests.xproj b/test/ArgumentForwardingTests/ArgumentForwardingTests.xproj index 5b9cd4886..6ccc31090 100644 --- a/test/ArgumentForwardingTests/ArgumentForwardingTests.xproj +++ b/test/ArgumentForwardingTests/ArgumentForwardingTests.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 6973e08d-11ec-49dc-82ef-d5effec7c6e9 Microsoft.DotNet.Tests.ArgumentForwarding @@ -14,5 +14,5 @@ 2.0 - + \ No newline at end of file diff --git a/test/ArgumentForwardingTests/project.json b/test/ArgumentForwardingTests/project.json index 8b99a663f..ccf9d856b 100644 --- a/test/ArgumentForwardingTests/project.json +++ b/test/ArgumentForwardingTests/project.json @@ -1,31 +1,32 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.ProjectModel": { + "target": "project" }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - - "Microsoft.DotNet.ProjectModel": { "target": "project" }, - "Microsoft.DotNet.Cli.Utils": { "target": "project" }, - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, - - "xunit": "2.1.0", - "dotnet-test-xunit": "1.0.0-dev-79755-47" + "Microsoft.DotNet.Cli.Utils": { + "target": "project" }, - - "frameworks": { - "netstandardapp1.5": { - "imports": [ - "dnxcore50", - "portable-net45+win8" - ] - } + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" }, - - "testRunner": "xunit", - - "scripts": { "precompile": "dotnet build ../ArgumentsReflector/project.json --framework netstandardapp1.5 --runtime %compile:RuntimeIdentifier% --output %compile:RuntimeOutputDir%" } + "xunit": "2.1.0", + "dotnet-test-xunit": "1.0.0-dev-79755-47" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ] + } + }, + "testRunner": "xunit", + "scripts": { + "precompile": "dotnet build ../ArgumentsReflector/project.json --framework netstandardapp1.5 --runtime %compile:RuntimeIdentifier% --output %compile:RuntimeOutputDir%" + } } diff --git a/test/ArgumentsReflector/Reflector.xproj b/test/ArgumentsReflector/Reflector.xproj index 06cbe2be6..5c40febb9 100644 --- a/test/ArgumentsReflector/Reflector.xproj +++ b/test/ArgumentsReflector/Reflector.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 6f2e6f25-b43b-495d-9ca5-7f207d1dd604 Microsoft.DotNet.Tests.ArgumentForwarding @@ -14,5 +14,5 @@ 2.0 - + \ No newline at end of file diff --git a/test/ArgumentsReflector/project.json b/test/ArgumentsReflector/project.json index b78dfacbe..6869effa2 100644 --- a/test/ArgumentsReflector/project.json +++ b/test/ArgumentsReflector/project.json @@ -1,18 +1,17 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library" : "1.0.0-rc2-23901" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" - } - }, - - "content": ["reflector_cmd.cmd"] + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": "dnxcore50" + } + }, + "content": [ + "reflector_cmd.cmd" + ] } diff --git a/test/EndToEnd/EndToEnd.xproj b/test/EndToEnd/EndToEnd.xproj index cfee5a0ea..d3dcb255f 100644 --- a/test/EndToEnd/EndToEnd.xproj +++ b/test/EndToEnd/EndToEnd.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 65741cb1-8aee-4c66-8198-10a7ea0e4258 Microsoft.DotNet.Tests.EndToEnd @@ -17,5 +17,5 @@ - + \ No newline at end of file diff --git a/test/EndToEnd/project.json b/test/EndToEnd/project.json index 4bcbd9290..ef90e4e9e 100644 --- a/test/EndToEnd/project.json +++ b/test/EndToEnd/project.json @@ -3,20 +3,21 @@ "compilationOptions": { "emitEntryPoint": true }, - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - - "Microsoft.DotNet.ProjectModel": { "target": "project" }, - "Microsoft.DotNet.Cli.Utils": { "target": "project" }, - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, - + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.ProjectModel": { + "target": "project" + }, + "Microsoft.DotNet.Cli.Utils": { + "target": "project" + }, + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + }, "xunit": "2.1.0", "xunit.netcore.extensions": "1.0.0-prerelease-*", "dotnet-test-xunit": "1.0.0-dev-91790-12" }, - "frameworks": { "netstandardapp1.5": { "imports": [ @@ -25,6 +26,5 @@ ] } }, - "testRunner": "xunit" } diff --git a/test/Installer/Microsoft.DotNet.Cli.Msi.Tests/Microsoft.DotNet.Cli.Msi.Tests.xproj b/test/Installer/Microsoft.DotNet.Cli.Msi.Tests/Microsoft.DotNet.Cli.Msi.Tests.xproj index 64ee27ba4..989b970a6 100644 --- a/test/Installer/Microsoft.DotNet.Cli.Msi.Tests/Microsoft.DotNet.Cli.Msi.Tests.xproj +++ b/test/Installer/Microsoft.DotNet.Cli.Msi.Tests/Microsoft.DotNet.Cli.Msi.Tests.xproj @@ -4,7 +4,7 @@ 14.0.23107 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 0B31C336-149D-471A-B7B1-27B0F1E80F83 Microsoft.DotNet.Cli.Msi.Tests @@ -14,5 +14,5 @@ 2.0 - + \ No newline at end of file diff --git a/test/Microsoft.DotNet.Cli.Utils.Tests/Microsoft.DotNet.Cli.Utils.Tests.xproj b/test/Microsoft.DotNet.Cli.Utils.Tests/Microsoft.DotNet.Cli.Utils.Tests.xproj index e7b914485..2d287f25f 100644 --- a/test/Microsoft.DotNet.Cli.Utils.Tests/Microsoft.DotNet.Cli.Utils.Tests.xproj +++ b/test/Microsoft.DotNet.Cli.Utils.Tests/Microsoft.DotNet.Cli.Utils.Tests.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 09c52f96-efdd-4448-95ec-6d362dd60baa Microsoft.DotNet.Cli.Utils @@ -14,5 +14,5 @@ 2.0 - + \ No newline at end of file diff --git a/test/Microsoft.DotNet.Cli.Utils.Tests/project.json b/test/Microsoft.DotNet.Cli.Utils.Tests/project.json index 97536b3c7..02638b27b 100644 --- a/test/Microsoft.DotNet.Cli.Utils.Tests/project.json +++ b/test/Microsoft.DotNet.Cli.Utils.Tests/project.json @@ -1,39 +1,37 @@ { - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.ProjectModel": { + "target": "project" }, - - "dependencies": { - "NETStandard.Library" : "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - - "Microsoft.DotNet.ProjectModel": { "target": "project" }, - "Microsoft.DotNet.Cli.Utils": { "target": "project" }, - - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, - - "moq.netcore": "4.4.0-beta8", - "xunit": "2.1.0", - "dotnet-test-xunit": "1.0.0-dev-79755-47" + "Microsoft.DotNet.Cli.Utils": { + "target": "project" }, - - "frameworks": { - "netstandardapp1.5": { - "imports": [ - "dnxcore50", - "portable-net45+win8" - ] - } + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" }, - - "content": [ - "../../TestAssets/TestProjects/OutputStandardOutputAndError/*", - "../../TestAssets/TestProjects/TestAppWithArgs/*", - "../../TestAssets/TestProjects/AppWithDirectAndToolDependency/**/*", - "../../TestAssets/TestProjects/AppWithDirectDependency/**/*", - "../../TestAssets/TestProjects/AppWithToolDependency/**/*" - ], - - "testRunner": "xunit" + "moq.netcore": "4.4.0-beta8", + "xunit": "2.1.0", + "dotnet-test-xunit": "1.0.0-dev-79755-47" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ] + } + }, + "content": [ + "../../TestAssets/TestProjects/OutputStandardOutputAndError/*", + "../../TestAssets/TestProjects/TestAppWithArgs/*", + "../../TestAssets/TestProjects/AppWithDirectAndToolDependency/**/*", + "../../TestAssets/TestProjects/AppWithDirectDependency/**/*", + "../../TestAssets/TestProjects/AppWithToolDependency/**/*" + ], + "testRunner": "xunit" } diff --git a/test/Microsoft.DotNet.Compiler.Common.Tests/Microsoft.DotNet.Compiler.Common.Tests.xproj b/test/Microsoft.DotNet.Compiler.Common.Tests/Microsoft.DotNet.Compiler.Common.Tests.xproj index 297d01c2b..8d39d9bb5 100644 --- a/test/Microsoft.DotNet.Compiler.Common.Tests/Microsoft.DotNet.Compiler.Common.Tests.xproj +++ b/test/Microsoft.DotNet.Compiler.Common.Tests/Microsoft.DotNet.Compiler.Common.Tests.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 44e7d1ac-dcf1-4a18-9c22-f09e6bb302b5 Microsoft.DotNet.Cli.Utils @@ -14,5 +14,5 @@ 2.0 - + \ No newline at end of file diff --git a/test/Microsoft.DotNet.Compiler.Common.Tests/project.json b/test/Microsoft.DotNet.Compiler.Common.Tests/project.json index 6b7d51697..6bcc81722 100644 --- a/test/Microsoft.DotNet.Compiler.Common.Tests/project.json +++ b/test/Microsoft.DotNet.Compiler.Common.Tests/project.json @@ -1,26 +1,26 @@ { - "version": "1.0.0-*", - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, - "Microsoft.DotNet.ProjectModel": { "target": "project" }, - "Microsoft.DotNet.Compiler.Common": { "target": "project" }, - - "xunit": "2.1.0", - "dotnet-test-xunit": "1.0.0-dev-79755-47" + "version": "1.0.0-*", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" }, - - "frameworks": { - "netstandardapp1.5": { - "imports": [ - "dnxcore50", - "portable-net45+win8" - ] - } + "Microsoft.DotNet.ProjectModel": { + "target": "project" }, - - "testRunner": "xunit" + "Microsoft.DotNet.Compiler.Common": { + "target": "project" + }, + "xunit": "2.1.0", + "dotnet-test-xunit": "1.0.0-dev-79755-47" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ] + } + }, + "testRunner": "xunit" } diff --git a/test/Microsoft.DotNet.ProjectModel.Tests/Microsoft.DotNet.ProjectModel.Tests.xproj b/test/Microsoft.DotNet.ProjectModel.Tests/Microsoft.DotNet.ProjectModel.Tests.xproj index f0b4029a5..b2001d9f8 100644 --- a/test/Microsoft.DotNet.ProjectModel.Tests/Microsoft.DotNet.ProjectModel.Tests.xproj +++ b/test/Microsoft.DotNet.ProjectModel.Tests/Microsoft.DotNet.ProjectModel.Tests.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 0745410a-6629-47eb-aab5-08d6288cad72 Microsoft.DotNet.ProjectModel.Tests @@ -17,5 +17,5 @@ - + \ No newline at end of file diff --git a/test/Microsoft.DotNet.ProjectModel.Tests/project.json b/test/Microsoft.DotNet.ProjectModel.Tests/project.json index edaaef649..25cf46739 100644 --- a/test/Microsoft.DotNet.ProjectModel.Tests/project.json +++ b/test/Microsoft.DotNet.ProjectModel.Tests/project.json @@ -1,10 +1,13 @@ { "version": "1.0.0-*", "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - "Microsoft.DotNet.ProjectModel": { "target": "project" }, - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.ProjectModel": { + "target": "project" + }, + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + }, "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-dev-91790-12" }, diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Microsoft.DotNet.Tools.Test.Utilities.xproj b/test/Microsoft.DotNet.Tools.Tests.Utilities/Microsoft.DotNet.Tools.Test.Utilities.xproj index 19010ace5..48a117951 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/Microsoft.DotNet.Tools.Test.Utilities.xproj +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Microsoft.DotNet.Tools.Test.Utilities.xproj @@ -5,7 +5,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + e4f46eab-b5a5-4e60-9b9d-40a1fadbf45c Microsoft.DotNet.Tools.Test.Utilities @@ -16,5 +16,5 @@ 2.0 - + diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Mock/FileSystemMockBuilder.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Mock/FileSystemMockBuilder.cs index 40ac2aa37..b5bfd5a56 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/Mock/FileSystemMockBuilder.cs +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Mock/FileSystemMockBuilder.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using Microsoft.Extensions.EnvironmentAbstractions; namespace Microsoft.Extensions.DependencyModel.Tests @@ -74,6 +75,11 @@ namespace Microsoft.Extensions.DependencyModel.Tests } return text; } + + public Stream OpenRead(string path) + { + return new MemoryStream(Encoding.UTF8.GetBytes(ReadAllText(path))); + } } private class DirectoryMock : IDirectory diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/project.json b/test/Microsoft.DotNet.Tools.Tests.Utilities/project.json index cf42e619c..ac4cff52e 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/project.json +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/project.json @@ -1,32 +1,29 @@ { - "version": "1.0.0-*", - "description": "Microsoft.DotNet.Tools.Tests.Utilities Class Library", - "compilationOptions": { - "keyFile": "../../tools/Key.snk" - }, - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.Collections.Immutable": "1.2.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - "FluentAssertions": "4.0.0", - "xunit": "2.1.0", - "dotnet-test-xunit": "1.0.0-dev-91790-12", - - "Microsoft.DotNet.TestFramework": "1.0.0-*", - "Microsoft.DotNet.Cli.Utils": "1.0.0-*", - "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537", - "Microsoft.DotNet.InternalAbstractions": { - "target": "project", - "version": "1.0.0-*" - } - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": [ - "dnxcore50", - "portable-net45+win8" - ] - } - }, + "version": "1.0.0-*", + "description": "Microsoft.DotNet.Tools.Tests.Utilities Class Library", + "compilationOptions": { + "keyFile": "../../tools/Key.snk" + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "System.Collections.Immutable": "1.2.0-rc2-23911", + "FluentAssertions": "4.0.0", + "xunit": "2.1.0", + "dotnet-test-xunit": "1.0.0-dev-91790-12", + "Microsoft.DotNet.TestFramework": "1.0.0-*", + "Microsoft.DotNet.Cli.Utils": "1.0.0-*", + "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537", + "Microsoft.DotNet.InternalAbstractions": { + "target": "project", + "version": "1.0.0-*" + } + }, + "frameworks": { + "netstandardapp1.5": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ] + } + } } diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs index b48d812e5..719559d7a 100644 --- a/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs +++ b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs @@ -96,14 +96,14 @@ namespace Microsoft.Extensions.DependencyModel.Tests } } }"); - context.RuntimeGraph.Should().Contain(p => p.Key == "osx.10.10-x64").Which - .Value.Should().BeEquivalentTo(); + context.RuntimeGraph.Should().Contain(p => p.Runtime == "osx.10.10-x64").Which + .Fallbacks.Should().BeEquivalentTo(); - context.RuntimeGraph.Should().Contain(p => p.Key == "osx.10.11-x64").Which - .Value.Should().BeEquivalentTo("osx"); + context.RuntimeGraph.Should().Contain(p => p.Runtime == "osx.10.11-x64").Which + .Fallbacks.Should().BeEquivalentTo("osx"); - context.RuntimeGraph.Should().Contain(p => p.Key == "rhel.7-x64").Which - .Value.Should().BeEquivalentTo("linux-x64", "unix"); + context.RuntimeGraph.Should().Contain(p => p.Runtime == "rhel.7-x64").Which + .Fallbacks.Should().BeEquivalentTo("linux-x64", "unix"); } [Fact] diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonWriterTests.cs b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonWriterTests.cs index 2091b157b..f4d4ad721 100644 --- a/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonWriterTests.cs +++ b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonWriterTests.cs @@ -37,7 +37,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests CompilationOptions compilationOptions = null, CompilationLibrary[] compileLibraries = null, RuntimeLibrary[] runtimeLibraries = null, - IReadOnlyList> runtimeGraph = null) + IReadOnlyList runtimeGraph = null) { return new DependencyContext( target ?? string.Empty, @@ -46,7 +46,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests compilationOptions ?? CompilationOptions.Default, compileLibraries ?? new CompilationLibrary[0], runtimeLibraries ?? new RuntimeLibrary[0], - runtimeGraph ?? new KeyValuePair[0] + runtimeGraph ?? new RuntimeFallbacks[0] ); } @@ -58,8 +58,8 @@ namespace Microsoft.Extensions.DependencyModel.Tests "Target/runtime", runtimeGraph: new[] { - new KeyValuePair("win7-x64", new [] { "win6", "win5"}), - new KeyValuePair("win8-x64", new [] { "win7-x64"}), + new RuntimeFallbacks("win7-x64", new [] { "win6", "win5"}), + new RuntimeFallbacks("win8-x64", new [] { "win7-x64"}), })); var rids = result.Should().HaveProperty("runtimes") diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextLoaderTests.cs b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextLoaderTests.cs new file mode 100644 index 000000000..3b6e88fea --- /dev/null +++ b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextLoaderTests.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyModel; +using FluentAssertions; +using Xunit; + +namespace Microsoft.Extensions.DependencyModel.Tests +{ + public class DependencyContextTests + { + [Fact] + public void MergeMergesLibraries() + { + var compilationLibraries = new[] + { + CreateCompilation("PackageA"), + CreateCompilation("PackageB"), + }; + + var runtimeLibraries = new[] + { + CreateRuntime("PackageA"), + CreateRuntime("PackageB"), + }; + + var compilationLibrariesRedist = new[] + { + CreateCompilation("PackageB"), + CreateCompilation("PackageC"), + }; + + var runtimeLibrariesRedist = new[] + { + CreateRuntime("PackageB"), + CreateRuntime("PackageC"), + }; + + var context = new DependencyContext( + "Framework", + "runtime", + true, + CompilationOptions.Default, + compilationLibraries, + runtimeLibraries, + new RuntimeFallbacks[] { }); + + var contextRedist = new DependencyContext( + "Framework", + "runtime", + true, + CompilationOptions.Default, + compilationLibrariesRedist, + runtimeLibrariesRedist, + new RuntimeFallbacks[] { }); + + var result = context.Merge(contextRedist); + + result.CompileLibraries.Should().BeEquivalentTo(new[] + { + compilationLibraries[0], + compilationLibraries[1], + compilationLibrariesRedist[1], + }); + + result.RuntimeLibraries.Should().BeEquivalentTo(new[] + { + runtimeLibraries[0], + runtimeLibraries[1], + runtimeLibrariesRedist[1], + }); + } + + public void MergeMergesRuntimeGraph() + { + var context = new DependencyContext( + "Framework", + "runtime", + true, + CompilationOptions.Default, + Enumerable.Empty(), + Enumerable.Empty(), + new RuntimeFallbacks[] + { + new RuntimeFallbacks("win8-x64", new [] { "win8" }), + }); + + var contextRedist = new DependencyContext( + "Framework", + "runtime", + true, + CompilationOptions.Default, + Enumerable.Empty(), + Enumerable.Empty(), + new RuntimeFallbacks[] + { + new RuntimeFallbacks("win8", new [] { "win7-x64", "win7-x86" }), + }); + + var result = context.Merge(contextRedist); + result.RuntimeGraph.Should().Contain(g => g.Runtime == "win8-x64"). + Subject.Fallbacks.Should().BeEquivalentTo("win8"); + result.RuntimeGraph.Should().Contain(g => g.Runtime == "win8"). + Subject.Fallbacks.Should().BeEquivalentTo("win7-x64", "win7-x86"); + } + + private CompilationLibrary CreateCompilation(string name) + { + return new CompilationLibrary( + "project", + name, + "1.1.1", + "HASH", + new string[] { }, + new Dependency[] { }, + false); + } + + private RuntimeLibrary CreateRuntime(string name) + { + return new RuntimeLibrary( + "project", + name, + "1.1.1", + "HASH", + new RuntimeAssembly[] { }, + new string[] { }, + new ResourceAssembly[] { }, + new RuntimeTarget[] { }, + new Dependency[] {}, + false); + } + } +} diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/Microsoft.Extensions.DependencyModel.Tests.xproj b/test/Microsoft.Extensions.DependencyModel.Tests/Microsoft.Extensions.DependencyModel.Tests.xproj index f6f7d2856..5b7cd1e50 100644 --- a/test/Microsoft.Extensions.DependencyModel.Tests/Microsoft.Extensions.DependencyModel.Tests.xproj +++ b/test/Microsoft.Extensions.DependencyModel.Tests/Microsoft.Extensions.DependencyModel.Tests.xproj @@ -4,7 +4,7 @@ 14.0.23107 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 4a4711d8-4312-49fc-87b5-4f183f4c6a51 Microsoft.Extensions.DependencyModel.Tests @@ -17,5 +17,5 @@ - + \ No newline at end of file diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/project.json b/test/Microsoft.Extensions.DependencyModel.Tests/project.json index 18a43cf55..f5f578e2e 100644 --- a/test/Microsoft.Extensions.DependencyModel.Tests/project.json +++ b/test/Microsoft.Extensions.DependencyModel.Tests/project.json @@ -5,16 +5,15 @@ "keyFile": "../../tools/Key.snk" }, "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + }, "FluentAssertions": "4.0.0", "moq.netcore": "4.4.0-beta8", "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-dev-91790-12" }, - "frameworks": { "netstandardapp1.5": { "imports": [ @@ -23,6 +22,5 @@ ] } }, - "testRunner": "xunit" -} \ No newline at end of file +} diff --git a/test/ScriptExecutorTests/ScriptExecutorTests.xproj b/test/ScriptExecutorTests/ScriptExecutorTests.xproj index e114297b9..b3c5668f3 100644 --- a/test/ScriptExecutorTests/ScriptExecutorTests.xproj +++ b/test/ScriptExecutorTests/ScriptExecutorTests.xproj @@ -4,7 +4,7 @@ 14.0.23107 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 833ffee1-7eed-4f51-8dfd-946d48833333 Microsoft.DotNet.Cli.Utils.ScriptExecutorTests @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/test/ScriptExecutorTests/project.json b/test/ScriptExecutorTests/project.json index 0317d0a3b..158d402c7 100644 --- a/test/ScriptExecutorTests/project.json +++ b/test/ScriptExecutorTests/project.json @@ -1,17 +1,19 @@ { "version": "1.0.0-*", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - - "Microsoft.DotNet.ProjectModel": { "target": "project" }, - "Microsoft.DotNet.Cli.Utils": { "target": "project" }, - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, - + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.ProjectModel": { + "target": "project" + }, + "Microsoft.DotNet.Cli.Utils": { + "target": "project" + }, + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + }, "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-dev-79755-47" }, - "frameworks": { "netstandardapp1.5": { "imports": [ @@ -20,10 +22,8 @@ ] } }, - "content": [ "../../TestAssets/TestProjects/TestApp/**/*" ], - "testRunner": "xunit" } diff --git a/test/dotnet-build.Tests/dotnet-build.Tests.xproj b/test/dotnet-build.Tests/dotnet-build.Tests.xproj index b23863bda..a3aca10c4 100644 --- a/test/dotnet-build.Tests/dotnet-build.Tests.xproj +++ b/test/dotnet-build.Tests/dotnet-build.Tests.xproj @@ -4,7 +4,7 @@ 14.0.23107 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 833ffee1-7eed-4f51-8dfd-946d48833333 Microsoft.DotNet.Tools.Builder.Tests @@ -17,5 +17,5 @@ - + \ No newline at end of file diff --git a/test/dotnet-build.Tests/project.json b/test/dotnet-build.Tests/project.json index c158034ef..23dce9bc7 100644 --- a/test/dotnet-build.Tests/project.json +++ b/test/dotnet-build.Tests/project.json @@ -1,19 +1,16 @@ { "version": "1.0.0-*", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + }, "Microsoft.DotNet.Cli.Utils": { "target": "project" }, - "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-dev-91790-12" }, - "frameworks": { "netstandardapp1.5": { "imports": [ @@ -22,6 +19,5 @@ ] } }, - "testRunner": "xunit" } diff --git a/test/dotnet-compile.Tests/dotnet-compile.Tests.xproj b/test/dotnet-compile.Tests/dotnet-compile.Tests.xproj index 109254497..93ffc1f97 100644 --- a/test/dotnet-compile.Tests/dotnet-compile.Tests.xproj +++ b/test/dotnet-compile.Tests/dotnet-compile.Tests.xproj @@ -4,7 +4,7 @@ 14.0.23107 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 833ffee1-7eed-4f51-8dfd-946d48893d6e Microsoft.DotNet.Tools.Compiler.Tests @@ -17,5 +17,5 @@ - + \ No newline at end of file diff --git a/test/dotnet-compile.Tests/project.json b/test/dotnet-compile.Tests/project.json index dd02d8697..c1c79bacd 100644 --- a/test/dotnet-compile.Tests/project.json +++ b/test/dotnet-compile.Tests/project.json @@ -1,19 +1,16 @@ { "version": "1.0.0-*", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + }, "Microsoft.DotNet.Cli.Utils": { "target": "project" }, - "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-dev-91790-12" }, - "frameworks": { "netstandardapp1.5": { "imports": [ @@ -22,7 +19,6 @@ ] } }, - "content": [ "../../TestAssets/TestProjects/DependencyContextValidator/**/*", "../../TestAssets/TestProjects/TestLibraryWithAnalyzer/*", @@ -31,6 +27,5 @@ "../../TestAssets/TestProjects/TestAppCompilationContext/**/*", "../../TestAssets/TestProjects/global.json" ], - "testRunner": "xunit" } diff --git a/test/dotnet-compile.UnitTests/dotnet-compile.UnitTests.xproj b/test/dotnet-compile.UnitTests/dotnet-compile.UnitTests.xproj index 458f8ad82..87bee84c4 100644 --- a/test/dotnet-compile.UnitTests/dotnet-compile.UnitTests.xproj +++ b/test/dotnet-compile.UnitTests/dotnet-compile.UnitTests.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 920b71d8-62da-4f5e-8a26-926c113f1d97 dotnet-compile.UnitTests @@ -17,5 +17,5 @@ - + \ No newline at end of file diff --git a/test/dotnet-compile.UnitTests/project.json b/test/dotnet-compile.UnitTests/project.json index 9813a2d85..e6cd5027b 100644 --- a/test/dotnet-compile.UnitTests/project.json +++ b/test/dotnet-compile.UnitTests/project.json @@ -1,23 +1,25 @@ { "version": "1.0.0-*", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - + "NETStandard.Library": "1.5.0-rc2-23911", "Microsoft.DotNet.Cli.Utils": { "target": "project" }, - - "dotnet": { "target": "project" }, - "Microsoft.DotNet.ProjectModel": { "target": "project" }, - + "dotnet": { + "target": "project" + }, + "Microsoft.Win32.Registry": { + "version": "4.0.0-rc2-23911", + "exclude": "Compile" + }, + "Microsoft.DotNet.ProjectModel": { + "target": "project" + }, "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-dev-91790-12", "moq.netcore": "4.4.0-beta8", "FluentAssertions": "4.2.2" }, - "frameworks": { "netstandardapp1.5": { "imports": [ @@ -26,10 +28,8 @@ ] } }, - "content": [ "../../TestAssets/TestProjects/TestAppWithLibrary/**/*" ], - "testRunner": "xunit" } diff --git a/test/dotnet-pack.Tests/dotnet-pack.Tests.xproj b/test/dotnet-pack.Tests/dotnet-pack.Tests.xproj index b0d2ba45f..0dd8575bb 100644 --- a/test/dotnet-pack.Tests/dotnet-pack.Tests.xproj +++ b/test/dotnet-pack.Tests/dotnet-pack.Tests.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 5fda6d37-3a3e-4333-ba5c-f0b28be316f4 dotnet-pack.Tests @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/test/dotnet-pack.Tests/project.json b/test/dotnet-pack.Tests/project.json index 178d883d3..86c244272 100644 --- a/test/dotnet-pack.Tests/project.json +++ b/test/dotnet-pack.Tests/project.json @@ -1,19 +1,17 @@ { "version": "1.0.0-*", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression.ZipFile": "4.0.1-rc2-23901", - - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, + "NETStandard.Library": "1.5.0-rc2-23911", + "System.IO.Compression.ZipFile": "4.0.1-rc2-23911", + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + }, "Microsoft.DotNet.Cli.Utils": { "target": "project" }, - "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-dev-79755-47" }, - "frameworks": { "netstandardapp1.5": { "imports": [ @@ -22,10 +20,8 @@ ] } }, - "content": [ "../../TestAssets/TestProjects/TestLibraryWithConfiguration/*" ], - "testRunner": "xunit" } diff --git a/test/dotnet-projectmodel-server.Tests/dotnet-projectmodel-server.Tests.xproj b/test/dotnet-projectmodel-server.Tests/dotnet-projectmodel-server.Tests.xproj index 29f474eb4..cc567f6fd 100644 --- a/test/dotnet-projectmodel-server.Tests/dotnet-projectmodel-server.Tests.xproj +++ b/test/dotnet-projectmodel-server.Tests/dotnet-projectmodel-server.Tests.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 11c77123-e4da-499f-8900-80c88c2c69f2 Microsoft.DotNet.ProjectModel.Server.Tests @@ -17,5 +17,5 @@ - + \ No newline at end of file diff --git a/test/dotnet-projectmodel-server.Tests/project.json b/test/dotnet-projectmodel-server.Tests/project.json index 42d8fd6f6..2c6dd69cb 100644 --- a/test/dotnet-projectmodel-server.Tests/project.json +++ b/test/dotnet-projectmodel-server.Tests/project.json @@ -1,20 +1,29 @@ { "dependencies": { - "dotnet": { "target": "project" }, - "Microsoft.DotNet.ProjectModel": { "target": "project" }, - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, + "dotnet": { + "target": "project" + }, + "Microsoft.Win32.Registry": { + "version": "4.0.0-rc2-23911", + "exclude": "Compile" + }, + "Microsoft.DotNet.ProjectModel": { + "target": "project" + }, + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + }, "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-dev-91790-12", - - "System.Net.NameResolution": "4.0.0-rc2-23901" + "System.Net.NameResolution": "4.0.0-rc2-23911" }, - "frameworks": { - "netstandardapp1.5": { - "imports": [ - "dnxcore50", - "portable-net45+win8" - ] - } - }, - "testRunner": "xunit" + "frameworks": { + "netstandardapp1.5": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ] + } + }, + "testRunner": "xunit" } diff --git a/test/dotnet-publish.Tests/dotnet-publish.Tests.xproj b/test/dotnet-publish.Tests/dotnet-publish.Tests.xproj index 5300386c1..55201e8b1 100644 --- a/test/dotnet-publish.Tests/dotnet-publish.Tests.xproj +++ b/test/dotnet-publish.Tests/dotnet-publish.Tests.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 386d412c-003c-47b1-8258-0e35865cb7c4 Microsoft.DotNet.Tools.Publish.Tests @@ -18,5 +18,5 @@ - + \ No newline at end of file diff --git a/test/dotnet-publish.Tests/project.json b/test/dotnet-publish.Tests/project.json index 0064f9349..5a746e56c 100644 --- a/test/dotnet-publish.Tests/project.json +++ b/test/dotnet-publish.Tests/project.json @@ -1,21 +1,18 @@ { "version": "1.0.0-*", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - + "NETStandard.Library": "1.5.0-rc2-23911", "Microsoft.DotNet.TestFramework": "1.0.0-*", - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + }, "Microsoft.DotNet.Cli.Utils": { "target": "project" }, - "xunit": "2.1.0", "xunit.netcore.extensions": "1.0.0-prerelease-*", "dotnet-test-xunit": "1.0.0-dev-91790-12" }, - "frameworks": { "netstandardapp1.5": { "imports": [ @@ -24,6 +21,5 @@ ] } }, - "testRunner": "xunit" } diff --git a/test/dotnet-resgen.Tests/dotnet-resgen.Tests.xproj b/test/dotnet-resgen.Tests/dotnet-resgen.Tests.xproj index 279ade660..7ca087810 100644 --- a/test/dotnet-resgen.Tests/dotnet-resgen.Tests.xproj +++ b/test/dotnet-resgen.Tests/dotnet-resgen.Tests.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 386d412c-003c-47b1-8258-0e35865cb7c4 Microsoft.DotNet.Tools.Resgen.Tests @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/test/dotnet-resgen.Tests/project.json b/test/dotnet-resgen.Tests/project.json index fd3d26275..270d4e747 100644 --- a/test/dotnet-resgen.Tests/project.json +++ b/test/dotnet-resgen.Tests/project.json @@ -1,20 +1,17 @@ { "version": "1.0.0-*", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + }, "Microsoft.DotNet.Cli.Utils": { "target": "project" }, - "xunit": "2.1.0", "xunit.netcore.extensions": "1.0.0-prerelease-*", "dotnet-test-xunit": "1.0.0-dev-79755-47" }, - "frameworks": { "netstandardapp1.5": { "imports": [ @@ -23,10 +20,8 @@ ] } }, - "content": [ "../../TestAssets/TestProjects/TestProjectWithResource/**/*" ], - "testRunner": "xunit" } diff --git a/test/dotnet-run.Tests/dotnet-run.Tests.xproj b/test/dotnet-run.Tests/dotnet-run.Tests.xproj index a48fde4f7..1921817f6 100644 --- a/test/dotnet-run.Tests/dotnet-run.Tests.xproj +++ b/test/dotnet-run.Tests/dotnet-run.Tests.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 35e3c2dc-9b38-4ec5-8dd7-c32458dc485f dotnet-run.Tests @@ -15,5 +15,5 @@ 2.0 - + \ No newline at end of file diff --git a/test/dotnet-run.Tests/project.json b/test/dotnet-run.Tests/project.json index ecbf0c5fc..2380359e2 100644 --- a/test/dotnet-run.Tests/project.json +++ b/test/dotnet-run.Tests/project.json @@ -1,19 +1,16 @@ { "version": "1.0.0-*", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + }, "Microsoft.DotNet.Cli.Utils": { "target": "project" }, - "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-dev-79755-47" }, - "frameworks": { "netstandardapp1.5": { "imports": [ @@ -22,6 +19,5 @@ ] } }, - "testRunner": "xunit" } diff --git a/test/dotnet-test.UnitTests/dotnet-test.UnitTests.xproj b/test/dotnet-test.UnitTests/dotnet-test.UnitTests.xproj index 968a14550..b54973d91 100644 --- a/test/dotnet-test.UnitTests/dotnet-test.UnitTests.xproj +++ b/test/dotnet-test.UnitTests/dotnet-test.UnitTests.xproj @@ -4,7 +4,7 @@ 14.0.24720 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 857274ac-e741-4266-a7fd-14dee0c1cc96 Microsoft.Dotnet.Tools.Test.Tests @@ -17,5 +17,5 @@ - + \ No newline at end of file diff --git a/test/dotnet-test.UnitTests/project.json b/test/dotnet-test.UnitTests/project.json index eb23b1792..81e469a45 100644 --- a/test/dotnet-test.UnitTests/project.json +++ b/test/dotnet-test.UnitTests/project.json @@ -1,18 +1,20 @@ { "version": "1.0.0-*", - "dependencies": { "Newtonsoft.Json": "7.0.1", - "NETStandard.Library": "1.0.0-rc2-23901", - - "dotnet": { "target": "project" }, - + "NETStandard.Library": "1.5.0-rc2-23911", + "dotnet": { + "target": "project" + }, + "Microsoft.Win32.Registry": { + "version": "4.0.0-rc2-23911", + "exclude": "Compile" + }, "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-dev-91790-12", "moq.netcore": "4.4.0-beta8", "FluentAssertions": "4.2.2" }, - "frameworks": { "netstandardapp1.5": { "imports": [ @@ -21,6 +23,5 @@ ] } }, - "testRunner": "xunit" } diff --git a/test/dotnet.Tests/dotnet.Tests.xproj b/test/dotnet.Tests/dotnet.Tests.xproj index 35608f3d2..5727980e1 100644 --- a/test/dotnet.Tests/dotnet.Tests.xproj +++ b/test/dotnet.Tests/dotnet.Tests.xproj @@ -4,7 +4,7 @@ 14.0.23107 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + cb710268-4a82-48e4-9531-faf1c8f78f4b Microsoft.DotNet.Tests @@ -17,5 +17,5 @@ - + \ No newline at end of file diff --git a/test/dotnet.Tests/project.json b/test/dotnet.Tests/project.json index 46da798d1..4443e85ea 100644 --- a/test/dotnet.Tests/project.json +++ b/test/dotnet.Tests/project.json @@ -1,20 +1,17 @@ { "version": "1.0.0-*", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "System.IO.Compression": "4.1.0-rc2-23901", - - "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + }, "Microsoft.DotNet.Cli.Utils": { "target": "project", "type": "build" }, - "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-dev-91790-12" }, - "frameworks": { "netstandardapp1.5": { "imports": [ @@ -23,12 +20,10 @@ ] } }, - "content": [ - "../../TestAssets/TestProjects/AppWithDirectAndToolDependency/**/*", - "../../TestAssets/TestProjects/AppWithDirectDependency/**/*", - "../../TestAssets/TestProjects/AppWithToolDependency/**/*" + "../../TestAssets/TestProjects/AppWithDirectAndToolDependency/**/*", + "../../TestAssets/TestProjects/AppWithDirectDependency/**/*", + "../../TestAssets/TestProjects/AppWithToolDependency/**/*" ], - "testRunner": "xunit" } diff --git a/tools/DebianPackageTool/scripts/debian_build_lib.sh b/tools/DebianPackageTool/scripts/debian_build_lib.sh index 0b3f4141d..97c4c0ead 100755 --- a/tools/DebianPackageTool/scripts/debian_build_lib.sh +++ b/tools/DebianPackageTool/scripts/debian_build_lib.sh @@ -142,6 +142,7 @@ _get_files_in_dir_tree(){ # Use Globstar expansion to enumerate all directories and files in the tree shopt -s globstar + shopt -s dotglob dir_tree_list=( "${root_dir}/"** ) # Build a new array with only the Files contained in $dir_tree_list diff --git a/tools/MultiProjectValidator/MultiProjectValidator.xproj b/tools/MultiProjectValidator/MultiProjectValidator.xproj index 27627c2bb..a940c18c6 100644 --- a/tools/MultiProjectValidator/MultiProjectValidator.xproj +++ b/tools/MultiProjectValidator/MultiProjectValidator.xproj @@ -4,7 +4,7 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + 08a68c6a-86f6-4ed2-89a7-b166d33e9f85 ProjectSanity @@ -14,5 +14,5 @@ 2.0 - + \ No newline at end of file diff --git a/tools/MultiProjectValidator/project.json b/tools/MultiProjectValidator/project.json index 59d9c5fb9..130929817 100644 --- a/tools/MultiProjectValidator/project.json +++ b/tools/MultiProjectValidator/project.json @@ -1,21 +1,19 @@ { - "version": "1.0.0-*", - "name": "pjvalidate", - "compilationOptions": { - "emitEntryPoint": true - }, - - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23901", - "Microsoft.DotNet.ProjectModel": "1.0.0-*", - "Microsoft.DotNet.Cli.Utils": "1.0.0-*" - }, - - "frameworks": { - "netstandardapp1.5": { - "imports": [ - "dnxcore50" - ] - } + "version": "1.0.0-*", + "name": "pjvalidate", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23911", + "Microsoft.DotNet.ProjectModel": "1.0.0-*", + "Microsoft.DotNet.Cli.Utils": "1.0.0-*" + }, + "frameworks": { + "netstandardapp1.5": { + "imports": [ + "dnxcore50" + ] } + } }