From 4350361f0cbefb6be642261add9151ac6cb7b5b6 Mon Sep 17 00:00:00 2001 From: Ahson Khan Date: Mon, 28 Jan 2019 22:14:35 -0800 Subject: [PATCH] Update M.E.DependencyModel to use S.T.Json source for netstandard2.0 (dotnet/core-setup#5009) * Update M.E.DependencyModel to use in-box S.T.Json for netstandard2.0 * Move StreamBufferWriter to a separate file. * Return array back to the pool on success. * Update test json and remove trailing commas to make it valid according to RFC. * Fix off-by-one error in skip, add a null string check, and remove other trailing commas. * Write end of object before flushing. * Add missing write end array to close the array block. * Update to latest source package with some API changes. * Address PR feedback. * Split DepContextJsonReader into common, newtonsoft, and netstandard. * Try to minimize the diff between Newtonsoft and S.T.Json. * Remove the .Common prefix for the source that is shared between the impls. * Explicitly add access modifiers and some code formatting fix. * Add debug asserts and add comment to where ReadToEnd comes from. * Mark methods as static where possible. * Update code formatting (use var, inline outs, etc.) * Use implicit cast to span and use try/finally block instead. * Always check to resize in case stream.Length changes. * Use an array-based buffer writer rather than one that supports stream. * Add a unified json reader to reduce code duplication. * Remove JsonTextReaderExtensions and cleanup some changes to keep the diff cleaner. * Skip comments by default. * Allow comments on the reader but throw FormatException to mimic current behavior. * Adjust the exception message comparion assert. * Use the latest LangVersion (7.3) required by the source package. * Rename partial files using Utf8JsonReader/JsonTextReader suffixes. * Address PR feedback - cleanup csproj and remove unused APIs. * Use JsonTextWriter (instead of JObject) and extract out common code by using UnifiedJsonWriter extract out common code by using UnifiedJsonWriterr * Rename Write to WriteCore to match the Read * Explicitly disallow comments and update test to assert JsonReaderException. * Add source package version to Versions.props. Commit migrated from https://github.com/dotnet/core-setup/commit/45f9401bf62faf0d3446cfd8681d35cc3487367a --- .../ArrayBufferWriter.cs | 125 +++++++ .../DependencyContextJsonReader.JsonTextReader.cs | 34 ++ .../DependencyContextJsonReader.Utf8JsonReader.cs | 93 +++++ .../DependencyContextJsonReader.cs | 217 +++++------ .../DependencyContextStrings.cs | 2 +- .../DependencyContextWriter.JsonTextWriter.cs | 31 ++ .../DependencyContextWriter.Utf8JsonWriter.cs | 31 ++ .../DependencyContextWriter.cs | 410 +++++++++++---------- .../JsonTextReaderExtensions.cs | 89 ----- .../Microsoft.Extensions.DependencyModel.csproj | 39 +- .../UnifiedJsonReader.JsonTextReader.cs | 125 +++++++ .../UnifiedJsonReader.Utf8JsonReader.cs | 166 +++++++++ .../UnifiedJsonWriter.JsonTextWriter.cs | 51 +++ .../UnifiedJsonWriter.Utf8JsonWriter.cs | 39 ++ .../DependencyContextJsonReaderTest.cs | 60 ++- .../DependencyContextLoaderTests.cs | 6 +- 16 files changed, 1088 insertions(+), 430 deletions(-) create mode 100644 src/installer/managed/Microsoft.Extensions.DependencyModel/ArrayBufferWriter.cs create mode 100644 src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.JsonTextReader.cs create mode 100644 src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.Utf8JsonReader.cs create mode 100644 src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.JsonTextWriter.cs create mode 100644 src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.Utf8JsonWriter.cs delete mode 100644 src/installer/managed/Microsoft.Extensions.DependencyModel/JsonTextReaderExtensions.cs create mode 100644 src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonReader.JsonTextReader.cs create mode 100644 src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonReader.Utf8JsonReader.cs create mode 100644 src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonWriter.JsonTextWriter.cs create mode 100644 src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonWriter.Utf8JsonWriter.cs diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/ArrayBufferWriter.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/ArrayBufferWriter.cs new file mode 100644 index 0000000..95dba9a --- /dev/null +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/ArrayBufferWriter.cs @@ -0,0 +1,125 @@ +// 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; +using System.Buffers; +using System.Diagnostics; +using System.IO; + +namespace Microsoft.Extensions.DependencyModel +{ + // TODO: Remove once we have https://github.com/dotnet/corefx/issues/34894 + internal class ArrayBufferWriter : IBufferWriter, IDisposable + { + private byte[] _rentedBuffer; + private int _written; + + private const int MinimumBufferSize = 256; + + public ArrayBufferWriter(int initialCapacity = MinimumBufferSize) + { + if (initialCapacity <= 0) + throw new ArgumentException(nameof(initialCapacity)); + + _rentedBuffer = ArrayPool.Shared.Rent(initialCapacity); + _written = 0; + } + + public void CopyTo(Stream stream) + { + if (_rentedBuffer == null) + throw new ObjectDisposedException(nameof(ArrayBufferWriter)); + + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + stream.Write(_rentedBuffer, 0, _written); + + _rentedBuffer.AsSpan(0, _written).Clear(); + _written = 0; + } + + public void Advance(int count) + { + if (_rentedBuffer == null) + throw new ObjectDisposedException(nameof(ArrayBufferWriter)); + + if (count < 0) + throw new ArgumentException(nameof(count)); + + if (_written > _rentedBuffer.Length - count) + throw new InvalidOperationException("Cannot advance past the end of the buffer."); + + _written += count; + } + + // Returns the rented buffer back to the pool + public void Dispose() + { + if (_rentedBuffer == null) + { + return; + } + + ArrayPool.Shared.Return(_rentedBuffer, clearArray: true); + _rentedBuffer = null; + _written = 0; + } + + public Memory GetMemory(int sizeHint = 0) + { + if (_rentedBuffer == null) + throw new ObjectDisposedException(nameof(ArrayBufferWriter)); + + if (sizeHint < 0) + throw new ArgumentException(nameof(sizeHint)); + + CheckAndResizeBuffer(sizeHint); + return _rentedBuffer.AsMemory(_written); + } + + public Span GetSpan(int sizeHint = 0) + { + if (_rentedBuffer == null) + throw new ObjectDisposedException(nameof(ArrayBufferWriter)); + + if (sizeHint < 0) + throw new ArgumentException(nameof(sizeHint)); + + CheckAndResizeBuffer(sizeHint); + return _rentedBuffer.AsSpan(_written); + } + + private void CheckAndResizeBuffer(int sizeHint) + { + Debug.Assert(sizeHint >= 0); + + if (sizeHint == 0) + { + sizeHint = MinimumBufferSize; + } + + int availableSpace = _rentedBuffer.Length - _written; + + if (sizeHint > availableSpace) + { + int growBy = sizeHint > _rentedBuffer.Length ? sizeHint : _rentedBuffer.Length; + + int newSize = checked(_rentedBuffer.Length + growBy); + + byte[] oldBuffer = _rentedBuffer; + + _rentedBuffer = ArrayPool.Shared.Rent(newSize); + + Debug.Assert(oldBuffer.Length >= _written); + Debug.Assert(_rentedBuffer.Length >= _written); + + oldBuffer.AsSpan(0, _written).CopyTo(_rentedBuffer); + ArrayPool.Shared.Return(oldBuffer, clearArray: true); + } + + Debug.Assert(_rentedBuffer.Length - _written > 0); + Debug.Assert(_rentedBuffer.Length - _written >= sizeHint); + } + } +} diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.JsonTextReader.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.JsonTextReader.cs new file mode 100644 index 0000000..2c1bfbf --- /dev/null +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.JsonTextReader.cs @@ -0,0 +1,34 @@ +// 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; +using System.IO; +using Newtonsoft.Json; + +namespace Microsoft.Extensions.DependencyModel +{ + public partial class DependencyContextJsonReader : IDependencyContextReader + { + public DependencyContext Read(Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + using (var streamReader = new StreamReader(stream)) + { + using (var reader = new JsonTextReader(streamReader)) + { + return Read(reader); + } + } + } + + private DependencyContext Read(JsonTextReader jsonReader) + { + var reader = new UnifiedJsonReader(jsonReader); + return ReadCore(reader); + } + } +} diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.Utf8JsonReader.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.Utf8JsonReader.cs new file mode 100644 index 0000000..88b04fc --- /dev/null +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.Utf8JsonReader.cs @@ -0,0 +1,93 @@ +// 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; +using System.Buffers; +using System.IO; +using System.Text.Json; + +namespace Microsoft.Extensions.DependencyModel +{ + public partial class DependencyContextJsonReader : IDependencyContextReader + { + public DependencyContext Read(Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + ArraySegment buffer = ReadToEnd(stream); + try + { + return Read(new Utf8JsonReader(buffer, isFinalBlock: true, state: default)); + } + finally + { + // Holds document content, clear it before returning it. + buffer.AsSpan().Clear(); + ArrayPool.Shared.Return(buffer.Array); + } + } + + private DependencyContext Read(Utf8JsonReader jsonReader) + { + var reader = new UnifiedJsonReader(jsonReader); + return ReadCore(reader); + } + + // Borrowed from https://github.com/dotnet/corefx/blob/ef23e3317ca6e83f1e959ab265a8e59fb8a6dcd9/src/System.Text.Json/src/System/Text/Json/Document/JsonDocument.Parse.cs#L176-L225 + private static ArraySegment ReadToEnd(Stream stream) + { + int written = 0; + byte[] rented = null; + + try + { + if (stream.CanSeek) + { + // Ask for 1 more than the length to avoid resizing later, + // which is unnecessary in the common case where the stream length doesn't change. + long expectedLength = Math.Max(0, stream.Length - stream.Position) + 1; + rented = ArrayPool.Shared.Rent(checked((int)expectedLength)); + } + else + { + rented = ArrayPool.Shared.Rent(UnseekableStreamInitialRentSize); + } + + int lastRead; + + do + { + if (rented.Length == written) + { + byte[] toReturn = rented; + rented = ArrayPool.Shared.Rent(checked(toReturn.Length * 2)); + Buffer.BlockCopy(toReturn, 0, rented, 0, toReturn.Length); + // Holds document content, clear it. + ArrayPool.Shared.Return(toReturn, clearArray: true); + } + + lastRead = stream.Read(rented, written, rented.Length - written); + written += lastRead; + } while (lastRead > 0); + + return new ArraySegment(rented, 0, written); + } + catch + { + if (rented != null) + { + // Holds document content, clear it before returning it. + rented.AsSpan(0, written).Clear(); + ArrayPool.Shared.Return(rented); + } + + throw; + } + } + + private const int UnseekableStreamInitialRentSize = 4096; + } +} diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs index d84abe0..d08cd57 100644 --- a/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs @@ -5,29 +5,13 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Newtonsoft.Json; namespace Microsoft.Extensions.DependencyModel { - public class DependencyContextJsonReader : IDependencyContextReader + public partial class DependencyContextJsonReader : IDependencyContextReader { private readonly IDictionary _stringPool = new Dictionary(); - public DependencyContext Read(Stream stream) - { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - using (var streamReader = new StreamReader(stream)) - { - using (var reader = new JsonTextReader(streamReader)) - { - return Read(reader); - } - } - } protected virtual void Dispose(bool disposing) { if (disposing) @@ -41,39 +25,39 @@ namespace Microsoft.Extensions.DependencyModel Dispose(true); } - private DependencyContext Read(JsonTextReader reader) + private DependencyContext ReadCore(UnifiedJsonReader reader) { - var runtime = string.Empty; - var framework = string.Empty; - var isPortable = true; + reader.ReadStartObject(); + + string runtime = string.Empty; + string framework = string.Empty; + bool isPortable = true; string runtimeTargetName = null; string runtimeSignature = null; - reader.ReadStartObject(); - CompilationOptions compilationOptions = null; List targets = null; Dictionary libraryStubs = null; List runtimeFallbacks = null; - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + while (reader.Read() && reader.IsTokenTypeProperty()) { - switch ((string)reader.Value) + switch (reader.GetStringValue()) { case DependencyContextStrings.RuntimeTargetPropertyName: - ReadRuntimeTarget(reader, out runtimeTargetName, out runtimeSignature); + ReadRuntimeTarget(ref reader, out runtimeTargetName, out runtimeSignature); break; case DependencyContextStrings.CompilationOptionsPropertName: - compilationOptions = ReadCompilationOptions(reader); + compilationOptions = ReadCompilationOptions(ref reader); break; case DependencyContextStrings.TargetsPropertyName: - targets = ReadTargets(reader); + targets = ReadTargets(ref reader); break; case DependencyContextStrings.LibrariesPropertyName: - libraryStubs = ReadLibraries(reader); + libraryStubs = ReadLibraries(ref reader); break; case DependencyContextStrings.RuntimesPropertyName: - runtimeFallbacks = ReadRuntimes(reader); + runtimeFallbacks = ReadRuntimes(ref reader); break; default: reader.Skip(); @@ -91,11 +75,11 @@ namespace Microsoft.Extensions.DependencyModel if (runtimeTargetName != null) { - var seperatorIndex = runtimeTargetName.IndexOf(DependencyContextStrings.VersionSeperator); - if (seperatorIndex > -1 && seperatorIndex < runtimeTargetName.Length) + int separatorIndex = runtimeTargetName.IndexOf(DependencyContextStrings.VersionSeparator); + if (separatorIndex > -1 && separatorIndex < runtimeTargetName.Length) { - runtime = runtimeTargetName.Substring(seperatorIndex + 1); - framework = runtimeTargetName.Substring(0, seperatorIndex); + runtime = runtimeTargetName.Substring(separatorIndex + 1); + framework = runtimeTargetName.Substring(0, separatorIndex); isPortable = false; } else @@ -106,7 +90,7 @@ namespace Microsoft.Extensions.DependencyModel Target compileTarget = null; - var ridlessTarget = targets.FirstOrDefault(t => !IsRuntimeTarget(t.Name)); + Target ridlessTarget = targets.FirstOrDefault(t => !IsRuntimeTarget(t.Name)); if (ridlessTarget != null) { compileTarget = ridlessTarget; @@ -130,7 +114,7 @@ namespace Microsoft.Extensions.DependencyModel runtimeFallbacks ?? Enumerable.Empty()); } - private Target SelectRuntimeTarget(List targets, string runtimeTargetName) + private static Target SelectRuntimeTarget(List targets, string runtimeTargetName) { Target target; @@ -155,21 +139,19 @@ namespace Microsoft.Extensions.DependencyModel return target; } - private bool IsRuntimeTarget(string name) + private static bool IsRuntimeTarget(string name) { - return name.Contains(DependencyContextStrings.VersionSeperator); + return name.Contains(DependencyContextStrings.VersionSeparator); } - private void ReadRuntimeTarget(JsonTextReader reader, out string runtimeTargetName, out string runtimeSignature) + private static void ReadRuntimeTarget(ref UnifiedJsonReader reader, out string runtimeTargetName, out string runtimeSignature) { runtimeTargetName = null; runtimeSignature = null; reader.ReadStartObject(); - string propertyName; - string propertyValue; - while (reader.TryReadStringProperty(out propertyName, out propertyValue)) + while (reader.TryReadStringProperty(out string propertyName, out string propertyValue)) { switch (propertyName) { @@ -185,7 +167,7 @@ namespace Microsoft.Extensions.DependencyModel reader.CheckEndObject(); } - private CompilationOptions ReadCompilationOptions(JsonTextReader reader) + private static CompilationOptions ReadCompilationOptions(ref UnifiedJsonReader reader) { IEnumerable defines = null; string languageVersion = null; @@ -202,9 +184,9 @@ namespace Microsoft.Extensions.DependencyModel reader.ReadStartObject(); - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + while (reader.Read() && reader.IsTokenTypeProperty()) { - switch ((string)reader.Value) + switch (reader.GetStringValue()) { case DependencyContextStrings.DefinesPropertyName: defines = reader.ReadStringArray(); @@ -216,31 +198,31 @@ namespace Microsoft.Extensions.DependencyModel platform = reader.ReadAsString(); break; case DependencyContextStrings.AllowUnsafePropertyName: - allowUnsafe = reader.ReadAsBoolean(); + allowUnsafe = reader.ReadAsNullableBoolean(); break; case DependencyContextStrings.WarningsAsErrorsPropertyName: - warningsAsErrors = reader.ReadAsBoolean(); + warningsAsErrors = reader.ReadAsNullableBoolean(); break; case DependencyContextStrings.OptimizePropertyName: - optimize = reader.ReadAsBoolean(); + optimize = reader.ReadAsNullableBoolean(); break; case DependencyContextStrings.KeyFilePropertyName: keyFile = reader.ReadAsString(); break; case DependencyContextStrings.DelaySignPropertyName: - delaySign = reader.ReadAsBoolean(); + delaySign = reader.ReadAsNullableBoolean(); break; case DependencyContextStrings.PublicSignPropertyName: - publicSign = reader.ReadAsBoolean(); + publicSign = reader.ReadAsNullableBoolean(); break; case DependencyContextStrings.DebugTypePropertyName: debugType = reader.ReadAsString(); break; case DependencyContextStrings.EmitEntryPointPropertyName: - emitEntryPoint = reader.ReadAsBoolean(); + emitEntryPoint = reader.ReadAsNullableBoolean(); break; case DependencyContextStrings.GenerateXmlDocumentationPropertyName: - generateXmlDocumentation = reader.ReadAsBoolean(); + generateXmlDocumentation = reader.ReadAsNullableBoolean(); break; default: reader.Skip(); @@ -265,15 +247,15 @@ namespace Microsoft.Extensions.DependencyModel generateXmlDocumentation); } - private List ReadTargets(JsonTextReader reader) + private List ReadTargets(ref UnifiedJsonReader reader) { reader.ReadStartObject(); var targets = new List(); - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + while (reader.Read() && reader.IsTokenTypeProperty()) { - targets.Add(ReadTarget(reader, (string)reader.Value)); + targets.Add(ReadTarget(ref reader, reader.GetStringValue())); } reader.CheckEndObject(); @@ -281,15 +263,15 @@ namespace Microsoft.Extensions.DependencyModel return targets; } - private Target ReadTarget(JsonTextReader reader, string targetName) + private Target ReadTarget(ref UnifiedJsonReader reader, string targetName) { reader.ReadStartObject(); var libraries = new List(); - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + while (reader.Read() && reader.IsTokenTypeProperty()) { - libraries.Add(ReadTargetLibrary(reader, (string)reader.Value)); + libraries.Add(ReadTargetLibrary(ref reader, reader.GetStringValue())); } reader.CheckEndObject(); @@ -301,7 +283,7 @@ namespace Microsoft.Extensions.DependencyModel }; } - private TargetLibrary ReadTargetLibrary(JsonTextReader reader, string targetLibraryName) + private TargetLibrary ReadTargetLibrary(ref UnifiedJsonReader reader, string targetLibraryName) { IEnumerable dependencies = null; List runtimes = null; @@ -313,30 +295,30 @@ namespace Microsoft.Extensions.DependencyModel reader.ReadStartObject(); - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + while (reader.Read() && reader.IsTokenTypeProperty()) { - switch ((string)reader.Value) + switch (reader.GetStringValue()) { case DependencyContextStrings.DependenciesPropertyName: - dependencies = ReadTargetLibraryDependencies(reader); + dependencies = ReadTargetLibraryDependencies(ref reader); break; case DependencyContextStrings.RuntimeAssembliesKey: - runtimes = ReadRuntimeFiles(reader); + runtimes = ReadRuntimeFiles(ref reader); break; case DependencyContextStrings.NativeLibrariesKey: - natives = ReadRuntimeFiles(reader); + natives = ReadRuntimeFiles(ref reader); break; case DependencyContextStrings.CompileTimeAssembliesKey: - compilations = ReadPropertyNames(reader); + compilations = ReadPropertyNames(ref reader); break; case DependencyContextStrings.RuntimeTargetsPropertyName: - runtimeTargets = ReadTargetLibraryRuntimeTargets(reader); + runtimeTargets = ReadTargetLibraryRuntimeTargets(ref reader); break; case DependencyContextStrings.ResourceAssembliesPropertyName: - resources = ReadTargetLibraryResources(reader); + resources = ReadTargetLibraryResources(ref reader); break; case DependencyContextStrings.CompilationOnlyPropertyName: - compileOnly = reader.ReadAsBoolean(); + compileOnly = reader.ReadAsNullableBoolean(); break; default: reader.Skip(); @@ -359,17 +341,13 @@ namespace Microsoft.Extensions.DependencyModel }; } - - - public IEnumerable ReadTargetLibraryDependencies(JsonTextReader reader) + private IEnumerable ReadTargetLibraryDependencies(ref UnifiedJsonReader reader) { var dependencies = new List(); - string name; - string version; reader.ReadStartObject(); - while (reader.TryReadStringProperty(out name, out version)) + while (reader.TryReadStringProperty(out string name, out string version)) { dependencies.Add(new Dependency(Pool(name), Pool(version))); } @@ -379,15 +357,15 @@ namespace Microsoft.Extensions.DependencyModel return dependencies; } - private List ReadPropertyNames(JsonTextReader reader) + private static List ReadPropertyNames(ref UnifiedJsonReader reader) { var runtimes = new List(); reader.ReadStartObject(); - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + while (reader.Read() && reader.IsTokenTypeProperty()) { - var libraryName = (string)reader.Value; + string libraryName = reader.GetStringValue(); reader.Skip(); runtimes.Add(libraryName); @@ -398,24 +376,22 @@ namespace Microsoft.Extensions.DependencyModel return runtimes; } - private List ReadRuntimeFiles(JsonTextReader reader) + private static List ReadRuntimeFiles(ref UnifiedJsonReader reader) { var runtimeFiles = new List(); reader.ReadStartObject(); - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + while (reader.Read() && reader.IsTokenTypeProperty()) { string assemblyVersion = null; string fileVersion = null; - var path = (string)reader.Value; + string path = reader.GetStringValue(); reader.ReadStartObject(); - string propertyName; - string propertyValue; - while (reader.TryReadStringProperty(out propertyName, out propertyValue)) + while (reader.TryReadStringProperty(out string propertyName, out string propertyValue)) { switch (propertyName) { @@ -438,22 +414,22 @@ namespace Microsoft.Extensions.DependencyModel return runtimeFiles; } - private List ReadTargetLibraryRuntimeTargets(JsonTextReader reader) + private List ReadTargetLibraryRuntimeTargets(ref UnifiedJsonReader reader) { var runtimeTargets = new List(); reader.ReadStartObject(); - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + while (reader.Read() && reader.IsTokenTypeProperty()) { - var runtimeTarget = new RuntimeTargetEntryStub(); - runtimeTarget.Path = (string)reader.Value; + var runtimeTarget = new RuntimeTargetEntryStub + { + Path = reader.GetStringValue() + }; reader.ReadStartObject(); - string propertyName; - string propertyValue; - while (reader.TryReadStringProperty(out propertyName, out propertyValue)) + while (reader.TryReadStringProperty(out string propertyName, out string propertyValue)) { switch (propertyName) { @@ -482,23 +458,20 @@ namespace Microsoft.Extensions.DependencyModel return runtimeTargets; } - private List ReadTargetLibraryResources(JsonTextReader reader) + private List ReadTargetLibraryResources(ref UnifiedJsonReader reader) { var resources = new List(); reader.ReadStartObject(); - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + while (reader.Read() && reader.IsTokenTypeProperty()) { - var path = (string)reader.Value; + string path = reader.GetStringValue(); string locale = null; reader.ReadStartObject(); - string propertyName; - string propertyValue; - - while (reader.TryReadStringProperty(out propertyName, out propertyValue)) + while (reader.TryReadStringProperty(out string propertyName, out string propertyValue)) { if (propertyName == DependencyContextStrings.LocalePropertyName) { @@ -519,17 +492,17 @@ namespace Microsoft.Extensions.DependencyModel return resources; } - private Dictionary ReadLibraries(JsonTextReader reader) + private Dictionary ReadLibraries(ref UnifiedJsonReader reader) { var libraries = new Dictionary(); reader.ReadStartObject(); - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + while (reader.Read() && reader.IsTokenTypeProperty()) { - var libraryName = (string)reader.Value; + string libraryName = reader.GetStringValue(); - libraries.Add(Pool(libraryName), ReadOneLibrary(reader)); + libraries.Add(Pool(libraryName), ReadOneLibrary(ref reader)); } reader.CheckEndObject(); @@ -537,7 +510,7 @@ namespace Microsoft.Extensions.DependencyModel return libraries; } - private LibraryStub ReadOneLibrary(JsonTextReader reader) + private LibraryStub ReadOneLibrary(ref UnifiedJsonReader reader) { string hash = null; string type = null; @@ -548,9 +521,9 @@ namespace Microsoft.Extensions.DependencyModel reader.ReadStartObject(); - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + while (reader.Read() && reader.IsTokenTypeProperty()) { - switch ((string)reader.Value) + switch (reader.GetStringValue()) { case DependencyContextStrings.Sha512PropertyName: hash = reader.ReadAsString(); @@ -559,7 +532,7 @@ namespace Microsoft.Extensions.DependencyModel type = reader.ReadAsString(); break; case DependencyContextStrings.ServiceablePropertyName: - serviceable = reader.ReadAsBoolean().GetValueOrDefault(false); + serviceable = reader.ReadAsBoolean(defaultValue: false); break; case DependencyContextStrings.PathPropertyName: path = reader.ReadAsString(); @@ -589,16 +562,16 @@ namespace Microsoft.Extensions.DependencyModel }; } - private List ReadRuntimes(JsonTextReader reader) + private static List ReadRuntimes(ref UnifiedJsonReader reader) { var runtimeFallbacks = new List(); reader.ReadStartObject(); - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + while (reader.Read() && reader.IsTokenTypeProperty()) { - var runtime = (string)reader.Value; - var fallbacks = reader.ReadStringArray(); + string runtime = reader.GetStringValue(); + string[] fallbacks = reader.ReadStringArray(); runtimeFallbacks.Add(new RuntimeFallbacks(runtime, fallbacks)); } @@ -621,23 +594,22 @@ namespace Microsoft.Extensions.DependencyModel private Library CreateLibrary(TargetLibrary targetLibrary, bool runtime, Dictionary libraryStubs) { - var nameWithVersion = targetLibrary.Name; - LibraryStub stub; + string nameWithVersion = targetLibrary.Name; - if (libraryStubs == null || !libraryStubs.TryGetValue(nameWithVersion, out stub)) + if (libraryStubs == null || !libraryStubs.TryGetValue(nameWithVersion, out LibraryStub stub)) { throw new InvalidOperationException($"Cannot find library information for {nameWithVersion}"); } - var seperatorPosition = nameWithVersion.IndexOf(DependencyContextStrings.VersionSeperator); + int separatorPosition = nameWithVersion.IndexOf(DependencyContextStrings.VersionSeparator); - var name = Pool(nameWithVersion.Substring(0, seperatorPosition)); - var version = Pool(nameWithVersion.Substring(seperatorPosition + 1)); + string name = Pool(nameWithVersion.Substring(0, separatorPosition)); + string version = Pool(nameWithVersion.Substring(separatorPosition + 1)); if (runtime) { // Runtime section of this library was trimmed by type:platform - var isCompilationOnly = targetLibrary.CompileOnly; + bool? isCompilationOnly = targetLibrary.CompileOnly; if (isCompilationOnly == true) { return null; @@ -647,9 +619,9 @@ namespace Microsoft.Extensions.DependencyModel var nativeLibraryGroups = new List(); if (targetLibrary.RuntimeTargets != null) { - foreach (var ridGroup in targetLibrary.RuntimeTargets.GroupBy(e => e.Rid)) + foreach (IGrouping ridGroup in targetLibrary.RuntimeTargets.GroupBy(e => e.Rid)) { - var groupRuntimeAssemblies = ridGroup + RuntimeFile[] groupRuntimeAssemblies = ridGroup .Where(e => e.Type == DependencyContextStrings.RuntimeAssetType) .Select(e => new RuntimeFile(e.Path, e.AssemblyVersion, e.FileVersion)) .ToArray(); @@ -661,7 +633,7 @@ namespace Microsoft.Extensions.DependencyModel groupRuntimeAssemblies.Where(a => Path.GetFileName(a.Path) != "_._"))); } - var groupNativeLibraries = ridGroup + RuntimeFile[] groupNativeLibraries = ridGroup .Where(e => e.Type == DependencyContextStrings.NativeAssetType) .Select(e => new RuntimeFile(e.Path, e.AssemblyVersion, e.FileVersion)) .ToArray(); @@ -697,11 +669,11 @@ namespace Microsoft.Extensions.DependencyModel serviceable: stub.Serviceable, path: stub.Path, hashPath: stub.HashPath, - runtimeStoreManifestName : stub.RuntimeStoreManifestName); + runtimeStoreManifestName: stub.RuntimeStoreManifestName); } else { - var assemblies = (targetLibrary.Compilations != null) ? targetLibrary.Compilations : Enumerable.Empty(); + IEnumerable assemblies = targetLibrary.Compilations ?? Enumerable.Empty(); return new CompilationLibrary( stub.Type, name, @@ -722,8 +694,7 @@ namespace Microsoft.Extensions.DependencyModel return null; } - string result; - if (!_stringPool.TryGetValue(s, out result)) + if (!_stringPool.TryGetValue(s, out string result)) { _stringPool[s] = s; result = s; diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextStrings.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextStrings.cs index ffe041c..2b1c0cf 100644 --- a/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextStrings.cs +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextStrings.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.DependencyModel { internal class DependencyContextStrings { - internal const char VersionSeperator = '/'; + internal const char VersionSeparator = '/'; internal const string CompileTimeAssembliesKey = "compile"; diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.JsonTextWriter.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.JsonTextWriter.cs new file mode 100644 index 0000000..d9230b5 --- /dev/null +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.JsonTextWriter.cs @@ -0,0 +1,31 @@ +// 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; +using System.IO; +using Newtonsoft.Json; + +namespace Microsoft.Extensions.DependencyModel +{ + public partial class DependencyContextWriter + { + public void Write(DependencyContext context, Stream stream) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + using (var writer = new StreamWriter(stream)) + { + using (var jsonWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented }) + { + WriteCore(context, new UnifiedJsonWriter(jsonWriter)); + } + } + } + } +} diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.Utf8JsonWriter.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.Utf8JsonWriter.cs new file mode 100644 index 0000000..919a44f --- /dev/null +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.Utf8JsonWriter.cs @@ -0,0 +1,31 @@ +// 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; +using System.IO; +using System.Text.Json; + +namespace Microsoft.Extensions.DependencyModel +{ + public partial class DependencyContextWriter + { + public void Write(DependencyContext context, Stream stream) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + using (var bufferWriter = new ArrayBufferWriter()) + { + var state = new JsonWriterState(options: new JsonWriterOptions { Indented = true }); + var jsonWriter = new Utf8JsonWriter(bufferWriter, state); + WriteCore(context, new UnifiedJsonWriter(jsonWriter)); + bufferWriter.CopyTo(stream); + } + } + } +} diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.cs index 9801634..ab2fb6a 100644 --- a/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.cs +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.cs @@ -4,136 +4,142 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Microsoft.Extensions.DependencyModel { - public class DependencyContextWriter + public partial class DependencyContextWriter { - public void Write(DependencyContext context, Stream stream) + private void WriteCore(DependencyContext context, UnifiedJsonWriter jsonWriter) { - if (context == null) + jsonWriter.WriteStartObject(); + WriteRuntimeTargetInfo(context, ref jsonWriter); + WriteCompilationOptions(context.CompilationOptions, ref jsonWriter); + WriteTargets(context, ref jsonWriter); + WriteLibraries(context, ref jsonWriter); + if (context.RuntimeGraph.Any()) { - throw new ArgumentNullException(nameof(context)); + WriteRuntimeGraph(context, ref jsonWriter); } - if (stream == null) + jsonWriter.WriteEndObject(); + jsonWriter.Flush(); + } + + private void WriteRuntimeTargetInfo(DependencyContext context, ref UnifiedJsonWriter jsonWriter) + { + jsonWriter.WriteStartObject(DependencyContextStrings.RuntimeTargetPropertyName, escape: false); + if (context.Target.IsPortable) { - throw new ArgumentNullException(nameof(stream)); + jsonWriter.WriteString(DependencyContextStrings.RuntimeTargetNamePropertyName, + context.Target.Framework, escape: false); } - using (var writer = new StreamWriter(stream)) + else { - using (var jsonWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented }) - { - Write(context).WriteTo(jsonWriter); - } + jsonWriter.WriteString(DependencyContextStrings.RuntimeTargetNamePropertyName, + context.Target.Framework + DependencyContextStrings.VersionSeparator + context.Target.Runtime, escape: false); } + jsonWriter.WriteString(DependencyContextStrings.RuntimeTargetSignaturePropertyName, + context.Target.RuntimeSignature, escape: false); + jsonWriter.WriteEndObject(); } - private JObject Write(DependencyContext context) + private void WriteRuntimeGraph(DependencyContext context, ref UnifiedJsonWriter jsonWriter) { - var contextObject = new JObject( - new JProperty(DependencyContextStrings.RuntimeTargetPropertyName, WriteRuntimeTargetInfo(context)), - new JProperty(DependencyContextStrings.CompilationOptionsPropertName, WriteCompilationOptions(context.CompilationOptions)), - new JProperty(DependencyContextStrings.TargetsPropertyName, WriteTargets(context)), - new JProperty(DependencyContextStrings.LibrariesPropertyName, WriteLibraries(context)) - ); - if (context.RuntimeGraph.Any()) + jsonWriter.WriteStartObject(DependencyContextStrings.RuntimesPropertyName, escape: false); + foreach (RuntimeFallbacks runtimeFallback in context.RuntimeGraph) { - contextObject.Add(new JProperty(DependencyContextStrings.RuntimesPropertyName, WriteRuntimeGraph(context))); + jsonWriter.WriteStartArray(runtimeFallback.Runtime); + foreach (string fallback in runtimeFallback.Fallbacks) + { + jsonWriter.WriteStringValue(fallback); + } + jsonWriter.WriteEndArray(); } - return contextObject; - } - - private JObject WriteRuntimeTargetInfo(DependencyContext context) - { - return new JObject( - new JProperty(DependencyContextStrings.RuntimeTargetNamePropertyName, - context.Target.IsPortable ? - context.Target.Framework : - context.Target.Framework + DependencyContextStrings.VersionSeperator + context.Target.Runtime - ), - new JProperty(DependencyContextStrings.RuntimeTargetSignaturePropertyName, - context.Target.RuntimeSignature - ) - ); + jsonWriter.WriteEndObject(); } - private JObject WriteRuntimeGraph(DependencyContext context) + private void WriteCompilationOptions(CompilationOptions compilationOptions, ref UnifiedJsonWriter jsonWriter) { - return new JObject( - context.RuntimeGraph.Select(g => new JProperty(g.Runtime, new JArray(g.Fallbacks))) - ); + jsonWriter.WriteStartObject(DependencyContextStrings.CompilationOptionsPropertName, escape: false); + if (compilationOptions.Defines?.Any() == true) + { + jsonWriter.WriteStartArray(DependencyContextStrings.DefinesPropertyName, escape: false); + foreach (string define in compilationOptions.Defines) + { + jsonWriter.WriteStringValue(define); + } + jsonWriter.WriteEndArray(); + } + AddStringPropertyIfNotNull(DependencyContextStrings.LanguageVersionPropertyName, compilationOptions.LanguageVersion, ref jsonWriter); + AddStringPropertyIfNotNull(DependencyContextStrings.PlatformPropertyName, compilationOptions.Platform, ref jsonWriter); + AddBooleanPropertyIfNotNull(DependencyContextStrings.AllowUnsafePropertyName, compilationOptions.AllowUnsafe, ref jsonWriter); + AddBooleanPropertyIfNotNull(DependencyContextStrings.WarningsAsErrorsPropertyName, compilationOptions.WarningsAsErrors, ref jsonWriter); + AddBooleanPropertyIfNotNull(DependencyContextStrings.OptimizePropertyName, compilationOptions.Optimize, ref jsonWriter); + AddStringPropertyIfNotNull(DependencyContextStrings.KeyFilePropertyName, compilationOptions.KeyFile, ref jsonWriter); + AddBooleanPropertyIfNotNull(DependencyContextStrings.DelaySignPropertyName, compilationOptions.DelaySign, ref jsonWriter); + AddBooleanPropertyIfNotNull(DependencyContextStrings.PublicSignPropertyName, compilationOptions.PublicSign, ref jsonWriter); + AddBooleanPropertyIfNotNull(DependencyContextStrings.EmitEntryPointPropertyName, compilationOptions.EmitEntryPoint, ref jsonWriter); + AddBooleanPropertyIfNotNull(DependencyContextStrings.GenerateXmlDocumentationPropertyName, compilationOptions.GenerateXmlDocumentation, ref jsonWriter); + AddStringPropertyIfNotNull(DependencyContextStrings.DebugTypePropertyName, compilationOptions.DebugType, ref jsonWriter); + jsonWriter.WriteEndObject(); } - private JObject WriteCompilationOptions(CompilationOptions compilationOptions) + private void AddStringPropertyIfNotNull(string name, string value, ref UnifiedJsonWriter jsonWriter) { - var o = new JObject(); - if (compilationOptions.Defines?.Any() == true) + if (value != null) { - o[DependencyContextStrings.DefinesPropertyName] = new JArray(compilationOptions.Defines); - } - AddPropertyIfNotNull(o, DependencyContextStrings.LanguageVersionPropertyName, compilationOptions.LanguageVersion); - AddPropertyIfNotNull(o, DependencyContextStrings.PlatformPropertyName, compilationOptions.Platform); - AddPropertyIfNotNull(o, DependencyContextStrings.AllowUnsafePropertyName, compilationOptions.AllowUnsafe); - AddPropertyIfNotNull(o, DependencyContextStrings.WarningsAsErrorsPropertyName, compilationOptions.WarningsAsErrors); - AddPropertyIfNotNull(o, DependencyContextStrings.OptimizePropertyName, compilationOptions.Optimize); - AddPropertyIfNotNull(o, DependencyContextStrings.KeyFilePropertyName, compilationOptions.KeyFile); - AddPropertyIfNotNull(o, DependencyContextStrings.DelaySignPropertyName, compilationOptions.DelaySign); - AddPropertyIfNotNull(o, DependencyContextStrings.PublicSignPropertyName, compilationOptions.PublicSign); - AddPropertyIfNotNull(o, DependencyContextStrings.EmitEntryPointPropertyName, compilationOptions.EmitEntryPoint); - AddPropertyIfNotNull(o, DependencyContextStrings.GenerateXmlDocumentationPropertyName, compilationOptions.GenerateXmlDocumentation); - AddPropertyIfNotNull(o, DependencyContextStrings.DebugTypePropertyName, compilationOptions.DebugType); - return o; + jsonWriter.WriteString(name, value, escape: true); + } } - private void AddPropertyIfNotNull(JObject o, string name, T value) + private void AddBooleanPropertyIfNotNull(string name, bool? value, ref UnifiedJsonWriter jsonWriter) { - if (value != null) + if (value.HasValue) { - o.Add(new JProperty(name, value)); + jsonWriter.WriteBoolean(name, value.Value, escape: true); } } - private JObject WriteTargets(DependencyContext context) + private void WriteTargets(DependencyContext context, ref UnifiedJsonWriter jsonWriter) { + jsonWriter.WriteStartObject(DependencyContextStrings.TargetsPropertyName, escape: false); if (context.Target.IsPortable) { - return new JObject( - new JProperty(context.Target.Framework, WritePortableTarget(context.RuntimeLibraries, context.CompileLibraries)) - ); + WritePortableTarget(context.Target.Framework, context.RuntimeLibraries, context.CompileLibraries, ref jsonWriter); } - - return new JObject( - new JProperty(context.Target.Framework, WriteTarget(context.CompileLibraries)), - new JProperty(context.Target.Framework + DependencyContextStrings.VersionSeperator + context.Target.Runtime, - WriteTarget(context.RuntimeLibraries)) - ); + else + { + WriteTarget(context.Target.Framework, context.CompileLibraries, ref jsonWriter); + WriteTarget(context.Target.Framework + DependencyContextStrings.VersionSeparator + context.Target.Runtime, + context.RuntimeLibraries, ref jsonWriter); + } + jsonWriter.WriteEndObject(); } - private JObject WriteTarget(IReadOnlyList libraries) + private void WriteTarget(string key, IReadOnlyList libraries, ref UnifiedJsonWriter jsonWriter) { - return new JObject( - libraries.Select(library => - new JProperty(library.Name + DependencyContextStrings.VersionSeperator + library.Version, WriteTargetLibrary(library)))); + jsonWriter.WriteStartObject(key); + int count = libraries.Count; + for (int i = 0; i < count; i++) + { + Library library = libraries[i]; + WriteTargetLibrary(library.Name + DependencyContextStrings.VersionSeparator + library.Version, library, ref jsonWriter); + } + jsonWriter.WriteEndObject(); } - private JObject WritePortableTarget(IReadOnlyList runtimeLibraries, IReadOnlyList compilationLibraries) + private void WritePortableTarget(string key, IReadOnlyList runtimeLibraries, IReadOnlyList compilationLibraries, ref UnifiedJsonWriter jsonWriter) { - var runtimeLookup = runtimeLibraries.ToDictionary(l => l.Name, StringComparer.OrdinalIgnoreCase); - var compileLookup = compilationLibraries.ToDictionary(l => l.Name, StringComparer.OrdinalIgnoreCase); + Dictionary runtimeLookup = runtimeLibraries.ToDictionary(l => l.Name, StringComparer.OrdinalIgnoreCase); + Dictionary compileLookup = compilationLibraries.ToDictionary(l => l.Name, StringComparer.OrdinalIgnoreCase); - var targetObject = new JObject(); + jsonWriter.WriteStartObject(key); - foreach (var packageName in runtimeLookup.Keys.Concat(compileLookup.Keys).Distinct()) + foreach (string packageName in runtimeLookup.Keys.Concat(compileLookup.Keys).Distinct()) { - RuntimeLibrary runtimeLibrary; - runtimeLookup.TryGetValue(packageName, out runtimeLibrary); + runtimeLookup.TryGetValue(packageName, out RuntimeLibrary runtimeLibrary); - CompilationLibrary compilationLibrary; - compileLookup.TryGetValue(packageName, out compilationLibrary); + compileLookup.TryGetValue(packageName, out CompilationLibrary compilationLibrary); if (compilationLibrary != null && runtimeLibrary != null) { @@ -146,138 +152,155 @@ namespace Microsoft.Extensions.DependencyModel Debug.Assert(compilationLibrary.RuntimeStoreManifestName == null); } - var library = (Library)compilationLibrary ?? (Library)runtimeLibrary; - targetObject.Add( - new JProperty(library.Name + DependencyContextStrings.VersionSeperator + library.Version, - WritePortableTargetLibrary(runtimeLibrary, compilationLibrary) - ) - ); + Library library = (Library)compilationLibrary ?? (Library)runtimeLibrary; + WritePortableTargetLibrary(library.Name + DependencyContextStrings.VersionSeparator + library.Version, + runtimeLibrary, compilationLibrary, ref jsonWriter); } - return targetObject; + jsonWriter.WriteEndObject(); } - private void AddCompilationAssemblies(JObject libraryObject, IEnumerable compilationAssemblies) + private void AddCompilationAssemblies(IEnumerable compilationAssemblies, ref UnifiedJsonWriter jsonWriter) { if (!compilationAssemblies.Any()) { return; } - libraryObject.Add(new JProperty(DependencyContextStrings.CompileTimeAssembliesKey, - WriteAssetList(compilationAssemblies)) - ); + + WriteAssetList(DependencyContextStrings.CompileTimeAssembliesKey, compilationAssemblies, ref jsonWriter); } - private void AddAssets(JObject libraryObject, string key, RuntimeAssetGroup group) + private void AddAssets(string key, RuntimeAssetGroup group, ref UnifiedJsonWriter jsonWriter) { if (group == null || !group.RuntimeFiles.Any()) { return; } - libraryObject.Add(new JProperty(key, - WriteAssetList(group.RuntimeFiles)) - ); + + WriteAssetList(key, group.RuntimeFiles, ref jsonWriter); } - private void AddDependencies(JObject libraryObject, IEnumerable dependencies) + private void AddDependencies(IEnumerable dependencies, ref UnifiedJsonWriter jsonWriter) { if (!dependencies.Any()) { return; } - libraryObject.AddFirst( - new JProperty(DependencyContextStrings.DependenciesPropertyName, - new JObject( - dependencies.Select(dependency => new JProperty(dependency.Name, dependency.Version)))) - ); + + jsonWriter.WriteStartObject(DependencyContextStrings.DependenciesPropertyName, escape: false); + foreach (Dependency dependency in dependencies) + { + jsonWriter.WriteString(dependency.Name, dependency.Version); + } + jsonWriter.WriteEndObject(); } - private void AddResourceAssemblies(JObject libraryObject, IEnumerable resourceAssemblies) + private void AddResourceAssemblies(IEnumerable resourceAssemblies, ref UnifiedJsonWriter jsonWriter) { if (!resourceAssemblies.Any()) { return; } - libraryObject.Add(DependencyContextStrings.ResourceAssembliesPropertyName, - new JObject(resourceAssemblies.Select(a => - new JProperty(NormalizePath(a.Path), new JObject(new JProperty(DependencyContextStrings.LocalePropertyName, a.Locale)))) - ) - ); + + jsonWriter.WriteStartObject(DependencyContextStrings.ResourceAssembliesPropertyName, escape: false); + foreach (ResourceAssembly resourceAssembly in resourceAssemblies) + { + jsonWriter.WriteStartObject(NormalizePath(resourceAssembly.Path)); + jsonWriter.WriteString(DependencyContextStrings.LocalePropertyName, resourceAssembly.Locale, escape: false); + jsonWriter.WriteEndObject(); + } + jsonWriter.WriteEndObject(); } - private JObject WriteTargetLibrary(Library library) + private void WriteTargetLibrary(string key, Library library, ref UnifiedJsonWriter jsonWriter) { - var runtimeLibrary = library as RuntimeLibrary; - if (runtimeLibrary != null) + if (library is RuntimeLibrary runtimeLibrary) { - var libraryObject = new JObject(); - AddDependencies(libraryObject, runtimeLibrary.Dependencies); + jsonWriter.WriteStartObject(key); + + AddDependencies(runtimeLibrary.Dependencies, ref jsonWriter); // Add runtime-agnostic assets - AddAssets(libraryObject, DependencyContextStrings.RuntimeAssembliesKey, runtimeLibrary.RuntimeAssemblyGroups.GetDefaultGroup()); - AddAssets(libraryObject, DependencyContextStrings.NativeLibrariesKey, runtimeLibrary.NativeLibraryGroups.GetDefaultGroup()); - AddResourceAssemblies(libraryObject, runtimeLibrary.ResourceAssemblies); + AddAssets(DependencyContextStrings.RuntimeAssembliesKey, runtimeLibrary.RuntimeAssemblyGroups.GetDefaultGroup(), ref jsonWriter); + AddAssets(DependencyContextStrings.NativeLibrariesKey, runtimeLibrary.NativeLibraryGroups.GetDefaultGroup(), ref jsonWriter); + AddResourceAssemblies(runtimeLibrary.ResourceAssemblies, ref jsonWriter); - return libraryObject; + jsonWriter.WriteEndObject(); } - - var compilationLibrary = library as CompilationLibrary; - if (compilationLibrary != null) + else if (library is CompilationLibrary compilationLibrary) { - var libraryObject = new JObject(); - AddDependencies(libraryObject, compilationLibrary.Dependencies); - AddCompilationAssemblies(libraryObject, compilationLibrary.Assemblies); - return libraryObject; + jsonWriter.WriteStartObject(key); + AddDependencies(compilationLibrary.Dependencies, ref jsonWriter); + AddCompilationAssemblies(compilationLibrary.Assemblies, ref jsonWriter); + jsonWriter.WriteEndObject(); + } + else + { + throw new NotSupportedException(); } - throw new NotSupportedException(); } - private JObject WritePortableTargetLibrary(RuntimeLibrary runtimeLibrary, CompilationLibrary compilationLibrary) + private void WritePortableTargetLibrary(string key, RuntimeLibrary runtimeLibrary, CompilationLibrary compilationLibrary, ref UnifiedJsonWriter jsonWriter) { - var libraryObject = new JObject(); + jsonWriter.WriteStartObject(key); var dependencies = new HashSet(); if (runtimeLibrary != null) { + dependencies.UnionWith(runtimeLibrary.Dependencies); + } + + if (compilationLibrary != null) + { + dependencies.UnionWith(compilationLibrary.Dependencies); + } + AddDependencies(dependencies, ref jsonWriter); + + + if (runtimeLibrary != null) + { // Add runtime-agnostic assets - AddAssets(libraryObject, DependencyContextStrings.RuntimeAssembliesKey, runtimeLibrary.RuntimeAssemblyGroups.GetDefaultGroup()); - AddAssets(libraryObject, DependencyContextStrings.NativeLibrariesKey, runtimeLibrary.NativeLibraryGroups.GetDefaultGroup()); - AddResourceAssemblies(libraryObject, runtimeLibrary.ResourceAssemblies); + AddAssets(DependencyContextStrings.RuntimeAssembliesKey, runtimeLibrary.RuntimeAssemblyGroups.GetDefaultGroup(), ref jsonWriter); + AddAssets(DependencyContextStrings.NativeLibrariesKey, runtimeLibrary.NativeLibraryGroups.GetDefaultGroup(), ref jsonWriter); + AddResourceAssemblies(runtimeLibrary.ResourceAssemblies, ref jsonWriter); // Add runtime-specific assets - var runtimeTargets = new JObject(); - AddRuntimeSpecificAssetGroups(runtimeTargets, DependencyContextStrings.RuntimeAssetType, runtimeLibrary.RuntimeAssemblyGroups); - AddRuntimeSpecificAssetGroups(runtimeTargets, DependencyContextStrings.NativeAssetType, runtimeLibrary.NativeLibraryGroups); - if (runtimeTargets.Count > 0) + bool wroteObjectStart = false; + wroteObjectStart = AddRuntimeSpecificAssetGroups(DependencyContextStrings.RuntimeAssetType, runtimeLibrary.RuntimeAssemblyGroups, wroteObjectStart, ref jsonWriter); + wroteObjectStart = AddRuntimeSpecificAssetGroups(DependencyContextStrings.NativeAssetType, runtimeLibrary.NativeLibraryGroups, wroteObjectStart, ref jsonWriter); + + if (wroteObjectStart) { - libraryObject.Add(DependencyContextStrings.RuntimeTargetsPropertyName, runtimeTargets); + jsonWriter.WriteEndObject(); } - - dependencies.UnionWith(runtimeLibrary.Dependencies); } if (compilationLibrary != null) { - AddCompilationAssemblies(libraryObject, compilationLibrary.Assemblies); - - dependencies.UnionWith(compilationLibrary.Dependencies); + AddCompilationAssemblies(compilationLibrary.Assemblies, ref jsonWriter); } - AddDependencies(libraryObject, dependencies); if (compilationLibrary != null && runtimeLibrary == null) { - libraryObject.Add(DependencyContextStrings.CompilationOnlyPropertyName, true); + jsonWriter.WriteBoolean(DependencyContextStrings.CompilationOnlyPropertyName, true, escape: false); } - return libraryObject; + + jsonWriter.WriteEndObject(); } - private void AddRuntimeSpecificAssetGroups(JObject runtimeTargets, string assetType, IEnumerable assetGroups) + private bool AddRuntimeSpecificAssetGroups(string assetType, IEnumerable assetGroups, bool wroteObjectStart, ref UnifiedJsonWriter jsonWriter) { - foreach (var group in assetGroups.Where(g => !string.IsNullOrEmpty(g.Runtime))) + IEnumerable groups = assetGroups.Where(g => !string.IsNullOrEmpty(g.Runtime)); + if (!wroteObjectStart && (groups.Count() > 0)) + { + jsonWriter.WriteStartObject(DependencyContextStrings.RuntimeTargetsPropertyName, escape: false); + wroteObjectStart = true; + } + foreach (RuntimeAssetGroup group in groups) { if (group.RuntimeFiles.Any()) { - AddRuntimeSpecificAssets(runtimeTargets, group.RuntimeFiles, group.Runtime, assetType); + AddRuntimeSpecificAssets(group.RuntimeFiles, group.Runtime, assetType, ref jsonWriter); } else { @@ -287,99 +310,114 @@ namespace Microsoft.Extensions.DependencyModel var pseudoPathFolder = assetType == DependencyContextStrings.RuntimeAssetType ? "lib" : "native"; - runtimeTargets[$"runtime/{group.Runtime}/{pseudoPathFolder}/_._"] = new JObject( - new JProperty(DependencyContextStrings.RidPropertyName, group.Runtime), - new JProperty(DependencyContextStrings.AssetTypePropertyName, assetType)); + + jsonWriter.WriteStartObject($"runtime/{group.Runtime}/{pseudoPathFolder}/_._"); + jsonWriter.WriteString(DependencyContextStrings.RidPropertyName, group.Runtime, escape: false); + jsonWriter.WriteString(DependencyContextStrings.AssetTypePropertyName, assetType, escape: false); + jsonWriter.WriteEndObject(); } } + return wroteObjectStart; } - private void AddRuntimeSpecificAssets(JObject target, IEnumerable assets, string runtime, string assetType) + private void AddRuntimeSpecificAssets(IEnumerable assets, string runtime, string assetType, ref UnifiedJsonWriter jsonWriter) { - foreach (var asset in assets) + foreach (RuntimeFile asset in assets) { - var asset_props = new JObject( - new JProperty(DependencyContextStrings.RidPropertyName, runtime), - new JProperty(DependencyContextStrings.AssetTypePropertyName, assetType) - ); + jsonWriter.WriteStartObject(NormalizePath(asset.Path)); + + jsonWriter.WriteString(DependencyContextStrings.RidPropertyName, runtime, escape: false); + jsonWriter.WriteString(DependencyContextStrings.AssetTypePropertyName, assetType, escape: false); if (asset.AssemblyVersion != null) { - asset_props.Add(DependencyContextStrings.AssemblyVersionPropertyName, asset.AssemblyVersion); + jsonWriter.WriteString(DependencyContextStrings.AssemblyVersionPropertyName, asset.AssemblyVersion, escape: false); } if (asset.FileVersion != null) { - asset_props.Add(DependencyContextStrings.FileVersionPropertyName, asset.FileVersion); + jsonWriter.WriteString(DependencyContextStrings.FileVersionPropertyName, asset.FileVersion, escape: false); } - target.Add(new JProperty(NormalizePath(asset.Path), asset_props)); + jsonWriter.WriteEndObject(); } } - private JObject WriteAssetList(IEnumerable assetPaths) + private void WriteAssetList(string key, IEnumerable assetPaths, ref UnifiedJsonWriter jsonWriter) { - return new JObject(assetPaths.Select(assembly => new JProperty(NormalizePath(assembly), new JObject()))); + jsonWriter.WriteStartObject(key, escape: false); + foreach (string assembly in assetPaths) + { + jsonWriter.WriteStartObject(NormalizePath(assembly)); + jsonWriter.WriteEndObject(); + } + jsonWriter.WriteEndObject(); } - private JObject WriteAssetList(IEnumerable runtimeFiles) + private void WriteAssetList(string key, IEnumerable runtimeFiles, ref UnifiedJsonWriter jsonWriter) { - var target = new JObject(); - foreach (var runtimeFile in runtimeFiles) + jsonWriter.WriteStartObject(key, escape: false); + + foreach (RuntimeFile runtimeFile in runtimeFiles) { - var fileJson = new JObject(); + jsonWriter.WriteStartObject(NormalizePath(runtimeFile.Path)); if (runtimeFile.AssemblyVersion != null) { - fileJson.Add(DependencyContextStrings.AssemblyVersionPropertyName, runtimeFile.AssemblyVersion); + jsonWriter.WriteString(DependencyContextStrings.AssemblyVersionPropertyName, runtimeFile.AssemblyVersion, escape: false); } if (runtimeFile.FileVersion != null) { - fileJson.Add(DependencyContextStrings.FileVersionPropertyName, runtimeFile.FileVersion); + jsonWriter.WriteString(DependencyContextStrings.FileVersionPropertyName, runtimeFile.FileVersion, escape: false); } - target.Add(new JProperty(NormalizePath(runtimeFile.Path), fileJson)); + jsonWriter.WriteEndObject(); } - return target; + jsonWriter.WriteEndObject(); } - private JObject WriteLibraries(DependencyContext context) + private void WriteLibraries(DependencyContext context, ref UnifiedJsonWriter jsonWriter) { - var allLibraries = + IEnumerable> allLibraries = context.RuntimeLibraries.Cast().Concat(context.CompileLibraries) - .GroupBy(library => library.Name + DependencyContextStrings.VersionSeperator + library.Version); + .GroupBy(library => library.Name + DependencyContextStrings.VersionSeparator + library.Version); - return new JObject(allLibraries.Select(libraries => new JProperty(libraries.Key, WriteLibrary(libraries.First())))); + jsonWriter.WriteStartObject(DependencyContextStrings.LibrariesPropertyName, escape: false); + foreach (IGrouping libraryGroup in allLibraries) + { + WriteLibrary(libraryGroup.Key, libraryGroup.First(), ref jsonWriter); + } + jsonWriter.WriteEndObject(); } - private JObject WriteLibrary(Library library) + private void WriteLibrary(string key, Library library, ref UnifiedJsonWriter jsonWriter) { - var libraryJson = new JObject( - new JProperty(DependencyContextStrings.TypePropertyName, library.Type), - new JProperty(DependencyContextStrings.ServiceablePropertyName, library.Serviceable), - new JProperty(DependencyContextStrings.Sha512PropertyName, library.Hash)); + jsonWriter.WriteStartObject(key); + jsonWriter.WriteString(DependencyContextStrings.TypePropertyName, library.Type, escape: false); + jsonWriter.WriteBoolean(DependencyContextStrings.ServiceablePropertyName, library.Serviceable, escape: false); + jsonWriter.WriteString(DependencyContextStrings.Sha512PropertyName, library.Hash, escape: false); if (library.Path != null) { - libraryJson.Add(new JProperty(DependencyContextStrings.PathPropertyName, library.Path)); + jsonWriter.WriteString(DependencyContextStrings.PathPropertyName, library.Path, escape: false); } if (library.HashPath != null) { - libraryJson.Add(new JProperty(DependencyContextStrings.HashPathPropertyName, library.HashPath)); + jsonWriter.WriteString(DependencyContextStrings.HashPathPropertyName, library.HashPath, escape: false); } if (library.RuntimeStoreManifestName != null) { - libraryJson.Add(new JProperty(DependencyContextStrings.RuntimeStoreManifestPropertyName, library.RuntimeStoreManifestName)); + jsonWriter.WriteString(DependencyContextStrings.RuntimeStoreManifestPropertyName, library.RuntimeStoreManifestName, escape: false); } - return libraryJson; + jsonWriter.WriteEndObject(); } - private string NormalizePath(string path) + private static string NormalizePath(string path) { return path.Replace('\\', '/'); } diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/JsonTextReaderExtensions.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/JsonTextReaderExtensions.cs deleted file mode 100644 index b088054..0000000 --- a/src/installer/managed/Microsoft.Extensions.DependencyModel/JsonTextReaderExtensions.cs +++ /dev/null @@ -1,89 +0,0 @@ -// 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; -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace Microsoft.Extensions.DependencyModel -{ - internal static class JsonTextReaderExtensions - { - internal static bool TryReadStringProperty(this JsonTextReader reader, out string name, out string value) - { - name = null; - value = null; - if (reader.Read() && reader.TokenType == JsonToken.PropertyName) - { - name = (string)reader.Value; - - if (reader.Read()) - { - if (reader.TokenType == JsonToken.String) - { - value = (string)reader.Value; - } - else - { - reader.Skip(); - } - } - - return true; - } - - return false; - } - - internal static void ReadStartObject(this JsonTextReader reader) - { - reader.Read(); - CheckStartObject(reader); - } - - internal static void CheckStartObject(this JsonTextReader reader) - { - if (reader.TokenType != JsonToken.StartObject) - { - throw CreateUnexpectedException(reader, "{"); - } - } - - internal static void CheckEndObject(this JsonTextReader reader) - { - if (reader.TokenType != JsonToken.EndObject) - { - throw CreateUnexpectedException(reader, "}"); - } - } - - internal static string[] ReadStringArray(this JsonTextReader reader) - { - reader.Read(); - if (reader.TokenType != JsonToken.StartArray) - { - throw CreateUnexpectedException(reader,"["); - } - - var items = new List(); - - while (reader.Read() && reader.TokenType == JsonToken.String) - { - items.Add((string)reader.Value); - } - - if (reader.TokenType != JsonToken.EndArray) - { - throw CreateUnexpectedException(reader, "]"); - } - - return items.ToArray(); - } - - internal static Exception CreateUnexpectedException(JsonTextReader reader, string expected) - { - return new FormatException($"Unexpected character encountered, excepted '{expected}' " + - $"at line {reader.LineNumber} position {reader.LinePosition} path {reader.Path}"); - } - } -} diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/Microsoft.Extensions.DependencyModel.csproj b/src/installer/managed/Microsoft.Extensions.DependencyModel/Microsoft.Extensions.DependencyModel.csproj index 965fe08..dfc8e12 100644 --- a/src/installer/managed/Microsoft.Extensions.DependencyModel/Microsoft.Extensions.DependencyModel.csproj +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/Microsoft.Extensions.DependencyModel.csproj @@ -5,30 +5,53 @@ Abstractions for reading `.deps` files. net451;netstandard1.3;netstandard1.6;netstandard2.0 netstandard1.3;netstandard1.6;netstandard2.0 + 7.3 + - 11.0.1 + true + + 3021 + + + $(MicrosoftBclJsonSourcesPackageVersion) + All + + + + + + + + + - - 9.0.1 - + + + + + + + + + + - diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonReader.JsonTextReader.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonReader.JsonTextReader.cs new file mode 100644 index 0000000..50f31cd --- /dev/null +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonReader.JsonTextReader.cs @@ -0,0 +1,125 @@ +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using Newtonsoft.Json; + +namespace Microsoft.Extensions.DependencyModel +{ + internal ref struct UnifiedJsonReader + { + private JsonTextReader _reader; + + public UnifiedJsonReader(JsonTextReader reader) + { + _reader = reader; + } + + public bool Read() => _reader.Read(); + + public string GetStringValue() => (string)_reader.Value; + + public bool IsTokenTypeProperty() + => _reader.TokenType == JsonToken.PropertyName; + + public bool TryReadStringProperty(out string name, out string value) + { + name = null; + value = null; + if (_reader.Read() && IsTokenTypeProperty()) + { + name = GetStringValue(); + + if (_reader.Read()) + { + if (_reader.TokenType == JsonToken.String) + { + value = GetStringValue(); + } + else + { + Skip(); + } + } + + return true; + } + + return false; + } + + public void ReadStartObject() + { + _reader.Read(); + CheckStartObject(); + } + + public void CheckStartObject() + { + if (_reader.TokenType != JsonToken.StartObject) + { + throw CreateUnexpectedException(_reader, "{"); + } + } + + public void CheckEndObject() + { + if (_reader.TokenType != JsonToken.EndObject) + { + throw CreateUnexpectedException(_reader, "}"); + } + } + + public string[] ReadStringArray() + { + _reader.Read(); + if (_reader.TokenType != JsonToken.StartArray) + { + throw CreateUnexpectedException(_reader, "["); + } + + var items = new List(); + + while (_reader.Read() && _reader.TokenType == JsonToken.String) + { + items.Add(GetStringValue()); + } + + if (_reader.TokenType != JsonToken.EndArray) + { + throw CreateUnexpectedException(_reader, "]"); + } + + return items.ToArray(); + } + + public void Skip() => _reader.Skip(); + + public string ReadAsString() + { + Debug.Assert(IsTokenTypeProperty()); + return _reader.ReadAsString(); + } + + public bool? ReadAsNullableBoolean() + { + Debug.Assert(IsTokenTypeProperty()); + return _reader.ReadAsBoolean(); + } + + public bool ReadAsBoolean(bool defaultValue) + { + Debug.Assert(IsTokenTypeProperty()); + bool? nullableBool = _reader.ReadAsBoolean(); + return nullableBool ?? defaultValue; + } + + private static Exception CreateUnexpectedException(JsonTextReader reader, string expected) + { + return new FormatException($"Unexpected character encountered, excepted '{expected}' " + + $"at line {reader.LineNumber} position {reader.LinePosition} path {reader.Path}"); + } + } +} diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonReader.Utf8JsonReader.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonReader.Utf8JsonReader.cs new file mode 100644 index 0000000..c968bb0 --- /dev/null +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonReader.Utf8JsonReader.cs @@ -0,0 +1,166 @@ +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.Json; + +namespace Microsoft.Extensions.DependencyModel +{ + internal ref struct UnifiedJsonReader + { + private Utf8JsonReader _reader; + + public UnifiedJsonReader(Utf8JsonReader reader) + { + _reader = reader; + } + + public bool Read() => _reader.Read(); + + public string GetStringValue() => _reader.GetString(); + + public bool IsTokenTypeProperty() + => _reader.TokenType == JsonTokenType.PropertyName; + + public bool TryReadStringProperty(out string name, out string value) + { + name = null; + value = null; + if (_reader.Read() && IsTokenTypeProperty()) + { + name = GetStringValue(); + + if (_reader.Read()) + { + if (_reader.TokenType == JsonTokenType.String) + { + value = GetStringValue(); + } + else + { + Skip(); + } + } + + return true; + } + + return false; + } + + public void ReadStartObject() + { + _reader.Read(); + CheckStartObject(); + } + + public void CheckStartObject() + { + if (_reader.TokenType != JsonTokenType.StartObject) + { + throw CreateUnexpectedException(ref _reader, "{"); + } + } + + public void CheckEndObject() + { + if (_reader.TokenType != JsonTokenType.EndObject) + { + throw CreateUnexpectedException(ref _reader, "}"); + } + } + + public string[] ReadStringArray() + { + _reader.Read(); + if (_reader.TokenType != JsonTokenType.StartArray) + { + throw CreateUnexpectedException(ref _reader, "["); + } + + var items = new List(); + + while (_reader.Read() && _reader.TokenType == JsonTokenType.String) + { + items.Add(GetStringValue()); + } + + if (_reader.TokenType != JsonTokenType.EndArray) + { + throw CreateUnexpectedException(ref _reader, "]"); + } + + return items.ToArray(); + } + + public void Skip() + { + if (IsTokenTypeProperty()) + { + _reader.Read(); + } + + if (_reader.TokenType == JsonTokenType.StartObject || _reader.TokenType == JsonTokenType.StartArray) + { + int depth = _reader.CurrentDepth; + while (_reader.Read() && depth <= _reader.CurrentDepth) + { + } + } + } + + public string ReadAsString() + { + Debug.Assert(IsTokenTypeProperty()); + _reader.Read(); + if (_reader.TokenType == JsonTokenType.Null) + { + return null; + } + if (_reader.TokenType != JsonTokenType.String) + { + throw CreateUnexpectedException(ref _reader, "a JSON string token"); + } + Debug.Assert(!IsTokenTypeProperty()); + return GetStringValue(); + } + + public bool? ReadAsNullableBoolean() + { + Debug.Assert(IsTokenTypeProperty()); + _reader.Read(); + if (_reader.TokenType == JsonTokenType.Null) + { + return null; + } + if (_reader.TokenType != JsonTokenType.True && _reader.TokenType != JsonTokenType.False) + { + throw CreateUnexpectedException(ref _reader, "a JSON true or false literal token"); + } + return _reader.GetBoolean(); + } + + public bool ReadAsBoolean(bool defaultValue) + { + Debug.Assert(IsTokenTypeProperty()); + _reader.Read(); + if (_reader.TokenType == JsonTokenType.Null) + { + return defaultValue; + } + if (_reader.TokenType != JsonTokenType.True && _reader.TokenType != JsonTokenType.False) + { + throw CreateUnexpectedException(ref _reader, "a JSON true or false literal token"); + } + return _reader.GetBoolean(); + } + + private static Exception CreateUnexpectedException(ref Utf8JsonReader reader, string expected) + { + return new FormatException($"Unexpected character encountered, excepted '{expected}' " + + $"at line {reader.CurrentState._lineNumber} position {reader.CurrentState._bytePositionInLine}"); + } + } +} diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonWriter.JsonTextWriter.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonWriter.JsonTextWriter.cs new file mode 100644 index 0000000..62a3628 --- /dev/null +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonWriter.JsonTextWriter.cs @@ -0,0 +1,51 @@ +// 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 Newtonsoft.Json; + +namespace Microsoft.Extensions.DependencyModel +{ + internal ref struct UnifiedJsonWriter + { + private readonly JsonTextWriter _writer; + + public UnifiedJsonWriter(JsonTextWriter writer) + { + _writer = writer; + } + + public void WriteStartObject() => _writer.WriteStartObject(); + public void WriteEndObject() => _writer.WriteEndObject(); + public void WriteStartArray() => _writer.WriteStartArray(); + public void WriteEndArray() => _writer.WriteEndArray(); + + public void Flush() => _writer.Flush(); + + public void WriteStartObject(string propertyName, bool escape = true) + { + _writer.WritePropertyName(propertyName, escape); + _writer.WriteStartObject(); + } + + public void WriteStartArray(string propertyName, bool escape = true) + { + _writer.WritePropertyName(propertyName, escape); + _writer.WriteStartArray(); + } + + public void WriteString(string propertyName, string value, bool escape = true) + { + _writer.WritePropertyName(propertyName, escape); + _writer.WriteValue(value); + } + + public void WriteBoolean(string propertyName, bool value, bool escape = true) + { + _writer.WritePropertyName(propertyName, escape); + _writer.WriteValue(value); + } + + public void WriteStringValue(string value, bool escape = true) + => _writer.WriteValue(value); + } +} diff --git a/src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonWriter.Utf8JsonWriter.cs b/src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonWriter.Utf8JsonWriter.cs new file mode 100644 index 0000000..a6f6b60 --- /dev/null +++ b/src/installer/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonWriter.Utf8JsonWriter.cs @@ -0,0 +1,39 @@ +// 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.Text.Json; + +namespace Microsoft.Extensions.DependencyModel +{ + internal ref struct UnifiedJsonWriter + { + private Utf8JsonWriter _writer; + + public UnifiedJsonWriter(Utf8JsonWriter writer) + { + _writer = writer; + } + + public void WriteStartObject() => _writer.WriteStartObject(); + public void WriteEndObject() => _writer.WriteEndObject(); + public void WriteStartArray() => _writer.WriteStartArray(); + public void WriteEndArray() => _writer.WriteEndArray(); + + public void Flush() => _writer.Flush(); + + public void WriteStartObject(string propertyName, bool escape = true) + => _writer.WriteStartObject(propertyName, escape); + + public void WriteStartArray(string propertyName, bool escape = true) + => _writer.WriteStartArray(propertyName, escape); + + public void WriteString(string propertyName, string value, bool escape = true) + => _writer.WriteString(propertyName, value, escape); + + public void WriteBoolean(string propertyName, bool value, bool escape = true) + => _writer.WriteBoolean(propertyName, value, escape); + + public void WriteStringValue(string value, bool escape = true) + => _writer.WriteStringValue(value, escape); + } +} diff --git a/src/installer/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs b/src/installer/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs index 2f951ab..60e7240 100644 --- a/src/installer/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs +++ b/src/installer/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs @@ -3,10 +3,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using System.Threading.Tasks; +using System.Text.Json; using FluentAssertions; using Xunit; -using System.Diagnostics; namespace Microsoft.Extensions.DependencyModel.Tests { @@ -30,7 +29,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""signature"":""target-signature"" }, ""targets"": { - "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {}, + "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {} } }"); context.Target.IsPortable.Should().BeFalse(); @@ -40,6 +39,27 @@ namespace Microsoft.Extensions.DependencyModel.Tests } [Fact] + public void ReadsRuntimeTargetInfoWithCommentsIsInvalid() + { + var exception = Assert.Throws(() => Read( +@"{ + ""runtimeTarget"": { + ""name"":"".NETCoreApp,Version=v1.0/osx.10.10-x64"", + ""signature"":""target-signature"" + }, + ""targets"": { + // Ignore comments + "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {} + /* + * Ignore multi-line comments + */ + } +}")); + + Assert.Equal("'/' is invalid after a value. Expected either ',', '}', or ']'. LineNumber: 6 | BytePositionInLine: 8.", exception.Message); + } + + [Fact] public void IgnoredUnknownPropertiesInRuntimeTarget() { var context = Read( @@ -51,7 +71,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""signature"":""target-signature"" }, ""targets"": { - "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {}, + "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {} } }"); context.Target.IsPortable.Should().BeFalse(); @@ -83,7 +103,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""type"": ""package"", ""serviceable"": false, ""sha512"": ""HASH-System.Banana"" - }, + } } }"; @@ -113,7 +133,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""type"": ""package"", ""serviceable"": false, ""sha512"": ""HASH-System.Banana"" - }, + } } }"; @@ -198,7 +218,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests var context = Read( @"{ ""targets"": { - "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {}, + "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {} }, ""runtimes"": { ""osx.10.10-x64"": [ ], @@ -251,7 +271,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""sha512"": ""HASH-System.Banana"", ""path"": ""PackagePath"", ""hashPath"": ""PachageHashPath"" - }, + } } }"); context.CompileLibraries.Should().HaveCount(2); @@ -348,7 +368,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""sha512"": ""HASH-System.Banana"", ""path"": null, ""hashPath"": null - }, + } } }"); context.CompileLibraries.Should().HaveCount(1); @@ -385,7 +405,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""type"": ""package"", ""serviceable"": false, ""sha512"": ""HASH-System.Banana"" - }, + } } }"); context.CompileLibraries.Should().HaveCount(1); @@ -434,7 +454,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""type"": ""package"", ""serviceable"": false, ""sha512"": ""HASH-System.Banana"" - }, + } } }"); context.CompileLibraries.Should().HaveCount(2); @@ -505,7 +525,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests }, ""libraries"":{ ""MyApp/1.0.1"": { - ""type"": ""project"", + ""type"": ""project"" }, ""System.Banana/1.0.0"": { ""type"": ""package"", @@ -514,7 +534,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""path"": ""PackagePath"", ""hashPath"": ""PackageHashPath"", ""runtimeStoreManifestName"": ""placeHolderManifest.xml"" - }, + } } }"; var context = Read(json); @@ -567,8 +587,8 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""System.Banana/1.0.0"": { ""runtimeTargets"": { ""runtime/win7-x64/lib/_._"": { ""assetType"": ""runtime"", ""rid"": ""win7-x64""}, - ""runtime/linux-x64/native/_._"": { ""assetType"": ""native"", ""rid"": ""linux-x64""}, - }, + ""runtime/linux-x64/native/_._"": { ""assetType"": ""native"", ""rid"": ""linux-x64""} + } } } }, @@ -577,7 +597,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""type"": ""package"", ""serviceable"": false, ""sha512"": ""HASH-System.Banana"" - }, + } } }"); context.CompileLibraries.Should().HaveCount(1); @@ -618,7 +638,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""type"": ""package"", ""serviceable"": false, ""sha512"": ""HASH-System.Banana"" - }, + } } }"); var package = context.RuntimeLibraries.Should().Contain(l => l.Name == "System.Banana").Subject; @@ -648,11 +668,11 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""optimize"": true }, ""targets"": { - "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {}, + "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {} } }"); context.CompilationOptions.AllowUnsafe.Should().Be(true); - context.CompilationOptions.Defines.Should().BeEquivalentTo(new [] {"MY", "DEFINES"}); + context.CompilationOptions.Defines.Should().BeEquivalentTo(new[] { "MY", "DEFINES" }); context.CompilationOptions.DelaySign.Should().Be(true); context.CompilationOptions.EmitEntryPoint.Should().Be(true); context.CompilationOptions.GenerateXmlDocumentation.Should().Be(true); @@ -685,7 +705,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""optimize"": true }, ""targets"": { - "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {}, + "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {} } }"); context.CompilationOptions.AllowUnsafe.Should().Be(true); diff --git a/src/installer/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextLoaderTests.cs b/src/installer/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextLoaderTests.cs index 14245fb..6375bc4 100644 --- a/src/installer/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextLoaderTests.cs +++ b/src/installer/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextLoaderTests.cs @@ -25,7 +25,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""signature"":""target-signature"" }, ""targets"": { - "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {}, + "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {} } }") .AddFile( @@ -34,7 +34,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""targets"": { "".NETCoreApp,Version=v1.0/osx.10.10-x64"": { - }, + } } }") .AddFile( @@ -59,7 +59,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests ""type"": ""package"", ""serviceable"": false, ""sha512"": ""HASH-System.Banana"" - }, + } } }") .Build(); -- 2.7.4