From ee6063b12b50ab94abbbb9082a62a03bb9fce403 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 2 Aug 2016 10:02:30 -0700 Subject: [PATCH] Use JsonTextReader in DependencyContextJsonReader (#3972) Using low level JsonTextReader in DependencyContextJsonReader class for performance reasons --- .../DependencyContextJsonReader.cs | 722 +++++++++++++----- .../JsonTextReaderExtensions.cs | 77 ++ .../DependencyContextJsonReaderTest.cs | 8 +- 3 files changed, 599 insertions(+), 208 deletions(-) create mode 100644 src/Microsoft.Extensions.DependencyModel/JsonTextReaderExtensions.cs diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs index 5d3ac12fa..6c40ae6d5 100644 --- a/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Microsoft.Extensions.DependencyModel { @@ -20,16 +19,15 @@ namespace Microsoft.Extensions.DependencyModel { throw new ArgumentNullException(nameof(stream)); } + using (var streamReader = new StreamReader(stream)) { using (var reader = new JsonTextReader(streamReader)) { - var root = JObject.Load(reader); - return Read(root); + return Read(reader); } } } - protected virtual void Dispose(bool disposing) { if (disposing) @@ -43,81 +41,77 @@ namespace Microsoft.Extensions.DependencyModel Dispose(true); } - private bool IsRuntimeTarget(string name) => name.Contains(DependencyContextStrings.VersionSeperator); - - private DependencyContext Read(JObject root) + private DependencyContext Read(JsonTextReader reader) { var runtime = string.Empty; - var target = string.Empty; + var framework = string.Empty; var isPortable = true; string runtimeTargetName = null; string runtimeSignature = null; - var runtimeTargetInfo = root[DependencyContextStrings.RuntimeTargetPropertyName]; + reader.ReadStartObject(); - // This fallback is temporary - if (runtimeTargetInfo is JValue) + CompilationOptions compilationOptions = null; + List targets = null; + Dictionary libraryStubs = null; + List runtimeFallbacks = null; + + while (reader.Read() && reader.TokenType == JsonToken.PropertyName) { - runtimeTargetName = runtimeTargetInfo.Value(); - } - else - { - var runtimeTargetObject = (JObject) runtimeTargetInfo; - runtimeTargetName = runtimeTargetObject?[DependencyContextStrings.RuntimeTargetNamePropertyName]?.Value(); - runtimeSignature = runtimeTargetObject?[DependencyContextStrings.RuntimeTargetSignaturePropertyName]?.Value(); - } - - var libraryStubs = ReadLibraryStubs((JObject)root[DependencyContextStrings.LibrariesPropertyName]); - var targetsObject = (JObject)root[DependencyContextStrings.TargetsPropertyName]; - - JObject runtimeTarget = null; - JObject compileTarget = null; - - if (targetsObject == null) - { - throw new FormatException("Dependency file does not have 'targets' section"); - } - - if (!string.IsNullOrEmpty(runtimeTargetName)) - { - runtimeTarget = (JObject)targetsObject[runtimeTargetName]; - if (runtimeTarget == null) + switch ((string)reader.Value) { - throw new FormatException($"Target with name {runtimeTargetName} not found"); + case DependencyContextStrings.RuntimeTargetPropertyName: + ReadRuntimeTarget(reader, out runtimeTargetName, out runtimeSignature); + break; + case DependencyContextStrings.CompilationOptionsPropertName: + compilationOptions = ReadCompilationOptions(reader); + break; + case DependencyContextStrings.TargetsPropertyName: + targets = ReadTargets(reader); + break; + case DependencyContextStrings.LibrariesPropertyName: + libraryStubs = ReadLibraries(reader); + break; + case DependencyContextStrings.RuntimesPropertyName: + runtimeFallbacks = ReadRuntimes(reader); + break; + } } - else - { - var runtimeTargetProperty = targetsObject.Properties() - .FirstOrDefault(p => IsRuntimeTarget(p.Name)); - runtimeTarget = (JObject)runtimeTargetProperty?.Value; - runtimeTargetName = runtimeTargetProperty?.Name; + if (compilationOptions == null) + { + compilationOptions = CompilationOptions.Default; } + Target runtimeTarget = SelectRuntimeTarget(targets, runtimeTargetName); + runtimeTargetName = runtimeTarget?.Name; + if (runtimeTargetName != null) { var seperatorIndex = runtimeTargetName.IndexOf(DependencyContextStrings.VersionSeperator); if (seperatorIndex > -1 && seperatorIndex < runtimeTargetName.Length) { runtime = runtimeTargetName.Substring(seperatorIndex + 1); - target = runtimeTargetName.Substring(0, seperatorIndex); + framework = runtimeTargetName.Substring(0, seperatorIndex); isPortable = false; } else { - target = runtimeTargetName; + framework = runtimeTargetName; } } - var ridlessTargetProperty = targetsObject.Properties().FirstOrDefault(p => !IsRuntimeTarget(p.Name)); - if (ridlessTargetProperty != null) + Target compileTarget = null; + + var ridlessTarget = targets.FirstOrDefault(t => !IsRuntimeTarget(t.Name)); + if (ridlessTarget != null) { - compileTarget = (JObject)ridlessTargetProperty.Value; + compileTarget = ridlessTarget; if (runtimeTarget == null) { runtimeTarget = compileTarget; - target = ridlessTargetProperty.Name; + framework = ridlessTarget.Name; } } @@ -127,65 +121,445 @@ namespace Microsoft.Extensions.DependencyModel } return new DependencyContext( - new TargetInfo(target, runtime, runtimeSignature, isPortable), - ReadCompilationOptions((JObject)root[DependencyContextStrings.CompilationOptionsPropertName]), - ReadLibraries(compileTarget, false, libraryStubs).Cast().ToArray(), - ReadLibraries(runtimeTarget, true, libraryStubs).Cast().ToArray(), - ReadRuntimeGraph((JObject)root[DependencyContextStrings.RuntimesPropertyName]).ToArray() - ); + new TargetInfo(framework, runtime, runtimeSignature, isPortable), + compilationOptions, + CreateLibraries(compileTarget?.Libraries, false, libraryStubs).Cast().ToArray(), + CreateLibraries(runtimeTarget.Libraries, true, libraryStubs).Cast().ToArray(), + runtimeFallbacks ?? Enumerable.Empty()); } - private IEnumerable ReadRuntimeGraph(JObject runtimes) + private Target SelectRuntimeTarget(List targets, string runtimeTargetName) { - if (runtimes == null) + Target target; + + if (targets == null || targets.Count == 0) { - yield break; + throw new FormatException("Dependency file does not have 'targets' section"); } - foreach (var pair in runtimes) + if (!string.IsNullOrEmpty(runtimeTargetName)) { - yield return new RuntimeFallbacks(pair.Key, pair.Value.Values().ToArray()); + target = targets.FirstOrDefault(t => t.Name == runtimeTargetName); + if (target == null) + { + throw new FormatException($"Target with name {runtimeTargetName} not found"); + } } + else + { + target = targets.FirstOrDefault(t => IsRuntimeTarget(t.Name)); + } + + return target; } - private CompilationOptions ReadCompilationOptions(JObject compilationOptionsObject) + private bool IsRuntimeTarget(string name) { - if (compilationOptionsObject == null) + return name.Contains(DependencyContextStrings.VersionSeperator); + } + + private void ReadRuntimeTarget(JsonTextReader reader, out string runtimeTargetName, out string runtimeSignature) + { + runtimeTargetName = null; + runtimeSignature = null; + + reader.ReadStartObject(); + + string propertyName; + string propertyValue; + while (reader.TryReadStringProperty(out propertyName, out propertyValue)) { - return CompilationOptions.Default; + switch (propertyName) + { + case DependencyContextStrings.RuntimeTargetNamePropertyName: + runtimeTargetName = propertyValue; + break; + case DependencyContextStrings.RuntimeTargetSignaturePropertyName: + runtimeSignature = propertyValue; + break; + default: + throw new FormatException($"Unknown property name '{propertyName}'"); + } } + reader.CheckEndObject(); + } + + private CompilationOptions ReadCompilationOptions(JsonTextReader reader) + { + IEnumerable defines = null; + string languageVersion = null; + string platform = null; + bool? allowUnsafe = null; + bool? warningsAsErrors = null; + bool? optimize = null; + string keyFile = null; + bool? delaySign = null; + bool? publicSign = null; + string debugType = null; + bool? emitEntryPoint = null; + bool? generateXmlDocumentation = null; + + reader.ReadStartObject(); + + while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + { + switch ((string)reader.Value) + { + case DependencyContextStrings.DefinesPropertyName: + defines = reader.ReadStringArray(); + break; + case DependencyContextStrings.LanguageVersionPropertyName: + languageVersion = reader.ReadAsString(); + break; + case DependencyContextStrings.PlatformPropertyName: + platform = reader.ReadAsString(); + break; + case DependencyContextStrings.AllowUnsafePropertyName: + allowUnsafe = reader.ReadAsBoolean(); + break; + case DependencyContextStrings.WarningsAsErrorsPropertyName: + warningsAsErrors = reader.ReadAsBoolean(); + break; + case DependencyContextStrings.OptimizePropertyName: + optimize = reader.ReadAsBoolean(); + break; + case DependencyContextStrings.KeyFilePropertyName: + keyFile = reader.ReadAsString(); + break; + case DependencyContextStrings.DelaySignPropertyName: + delaySign = reader.ReadAsBoolean(); + break; + case DependencyContextStrings.PublicSignPropertyName: + publicSign = reader.ReadAsBoolean(); + break; + case DependencyContextStrings.DebugTypePropertyName: + debugType = reader.ReadAsString(); + break; + case DependencyContextStrings.EmitEntryPointPropertyName: + emitEntryPoint = reader.ReadAsBoolean(); + break; + case DependencyContextStrings.GenerateXmlDocumentationPropertyName: + generateXmlDocumentation = reader.ReadAsBoolean(); + break; + default: + throw new FormatException($"Unknown property name '{reader.Value}'"); + } + } + + reader.CheckEndObject(); + return new CompilationOptions( - compilationOptionsObject[DependencyContextStrings.DefinesPropertyName]?.Values().ToArray() ?? Enumerable.Empty(), - // ToArray is here to prevent IEnumerable holding to json object graph - compilationOptionsObject[DependencyContextStrings.LanguageVersionPropertyName]?.Value(), - compilationOptionsObject[DependencyContextStrings.PlatformPropertyName]?.Value(), - compilationOptionsObject[DependencyContextStrings.AllowUnsafePropertyName]?.Value(), - compilationOptionsObject[DependencyContextStrings.WarningsAsErrorsPropertyName]?.Value(), - compilationOptionsObject[DependencyContextStrings.OptimizePropertyName]?.Value(), - compilationOptionsObject[DependencyContextStrings.KeyFilePropertyName]?.Value(), - compilationOptionsObject[DependencyContextStrings.DelaySignPropertyName]?.Value(), - compilationOptionsObject[DependencyContextStrings.PublicSignPropertyName]?.Value(), - compilationOptionsObject[DependencyContextStrings.DebugTypePropertyName]?.Value(), - compilationOptionsObject[DependencyContextStrings.EmitEntryPointPropertyName]?.Value(), - compilationOptionsObject[DependencyContextStrings.GenerateXmlDocumentationPropertyName]?.Value() - ); + defines ?? Enumerable.Empty(), + languageVersion, + platform, + allowUnsafe, + warningsAsErrors, + optimize, + keyFile, + delaySign, + publicSign, + debugType, + emitEntryPoint, + generateXmlDocumentation); } - private IEnumerable ReadLibraries(JObject librariesObject, bool runtime, Dictionary libraryStubs) + private List ReadTargets(JsonTextReader reader) { - if (librariesObject == null) + reader.ReadStartObject(); + + var targets = new List(); + + while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + { + targets.Add(ReadTarget(reader, (string)reader.Value)); + } + + reader.CheckEndObject(); + + return targets; + } + + private Target ReadTarget(JsonTextReader reader, string targetName) + { + reader.ReadStartObject(); + + var libraries = new List(); + + while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + { + libraries.Add(ReadTargetLibrary(reader, (string)reader.Value)); + } + + reader.CheckEndObject(); + + return new Target() + { + Name = targetName, + Libraries = libraries + }; + } + + private TargetLibrary ReadTargetLibrary(JsonTextReader reader, string targetLibraryName) + { + IEnumerable dependencies = null; + List runtimes = null; + List natives = null; + List compilations = null; + List runtimeTargets = null; + List resources = null; + bool? compileOnly = null; + + reader.ReadStartObject(); + + while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + { + switch ((string)reader.Value) + { + case DependencyContextStrings.DependenciesPropertyName: + dependencies = ReadTargetLibraryDependencies(reader); + break; + case DependencyContextStrings.RuntimeAssembliesKey: + runtimes = ReadPropertyNames(reader); + break; + case DependencyContextStrings.NativeLibrariesKey: + natives = ReadPropertyNames(reader); + break; + case DependencyContextStrings.CompileTimeAssembliesKey: + compilations = ReadPropertyNames(reader); + break; + case DependencyContextStrings.RuntimeTargetsPropertyName: + runtimeTargets = ReadTargetLibraryRuntimeTargets(reader); + break; + case DependencyContextStrings.ResourceAssembliesPropertyName: + resources = ReadTargetLibraryResources(reader); + break; + case DependencyContextStrings.CompilationOnlyPropertyName: + compileOnly = reader.ReadAsBoolean(); + break; + default: + throw new FormatException($"Unknown property name '{reader.Value}'"); + } + } + + reader.CheckEndObject(); + + return new TargetLibrary() + { + Name = targetLibraryName, + Dependencies = dependencies ?? Enumerable.Empty(), + Runtimes = runtimes, + Natives = natives, + Compilations = compilations, + RuntimeTargets = runtimeTargets, + Resources = resources, + CompileOnly = compileOnly + }; + } + + + + public IEnumerable ReadTargetLibraryDependencies(JsonTextReader reader) + { + var dependencies = new List(); + string name; + string version; + + reader.ReadStartObject(); + + while (reader.TryReadStringProperty(out name, out version)) + { + dependencies.Add(new Dependency(Pool(name), Pool(version))); + } + + reader.CheckEndObject(); + + return dependencies; + } + + private List ReadPropertyNames(JsonTextReader reader) + { + var runtimes = new List(); + + reader.ReadStartObject(); + + while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + { + var libraryName = (string)reader.Value; + reader.Skip(); + + runtimes.Add(libraryName); + } + + reader.CheckEndObject(); + + return runtimes; + } + + private List ReadTargetLibraryRuntimeTargets(JsonTextReader reader) + { + var runtimeTargets = new List(); + + reader.ReadStartObject(); + + while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + { + var runtimeTarget = new RuntimeTargetEntryStub(); + runtimeTarget.Path = (string)reader.Value; + + reader.ReadStartObject(); + + string propertyName; + string propertyValue; + while (reader.TryReadStringProperty(out propertyName, out propertyValue)) + { + switch (propertyName) + { + case DependencyContextStrings.RidPropertyName: + runtimeTarget.Rid = Pool(propertyValue); + break; + case DependencyContextStrings.AssetTypePropertyName: + runtimeTarget.Type = Pool(propertyValue); + break; + default: + throw new FormatException($"Unknown property name '{propertyName}'"); + } + } + + reader.CheckEndObject(); + + runtimeTargets.Add(runtimeTarget); + } + + reader.CheckEndObject(); + + return runtimeTargets; + } + + private List ReadTargetLibraryResources(JsonTextReader reader) + { + var resources = new List(); + + reader.ReadStartObject(); + + while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + { + var path = (string)reader.Value; + string locale = null; + + reader.ReadStartObject(); + + string propertyName; + string propertyValue; + + while (reader.TryReadStringProperty(out propertyName, out propertyValue)) + { + if (propertyName == DependencyContextStrings.LocalePropertyName) + { + locale = propertyValue; + } + } + + reader.CheckEndObject(); + + if (locale != null) + { + resources.Add(new ResourceAssembly(path, Pool(locale))); + } + } + + reader.CheckEndObject(); + + return resources; + } + + private Dictionary ReadLibraries(JsonTextReader reader) + { + var libraries = new Dictionary(); + + reader.ReadStartObject(); + + while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + { + var libraryName = (string)reader.Value; + + libraries.Add(Pool(libraryName), ReadOneLibrary(reader)); + } + + reader.CheckEndObject(); + + return libraries; + } + + private LibraryStub ReadOneLibrary(JsonTextReader reader) + { + string hash = null; + string type = null; + bool serviceable = false; + + reader.ReadStartObject(); + + while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + { + switch ((string)reader.Value) + { + case DependencyContextStrings.Sha512PropertyName: + hash = reader.ReadAsString(); + break; + case DependencyContextStrings.TypePropertyName: + type = reader.ReadAsString(); + break; + case DependencyContextStrings.ServiceablePropertyName: + serviceable = reader.ReadAsBoolean().GetValueOrDefault(false); + break; + default: + throw new FormatException($"Unknown property name '{reader.Value}'"); + } + } + + reader.CheckEndObject(); + + return new LibraryStub() + { + Hash = hash, + Type = Pool(type), + Serviceable = serviceable + }; + } + + private List ReadRuntimes(JsonTextReader reader) + { + var runtimeFallbacks = new List(); + + reader.ReadStartObject(); + + while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + { + var runtime = (string)reader.Value; + var fallbacks = reader.ReadStringArray(); + + runtimeFallbacks.Add(new RuntimeFallbacks(runtime, fallbacks)); + } + + reader.CheckEndObject(); + + return runtimeFallbacks; + } + + private IEnumerable CreateLibraries(IEnumerable libraries, bool runtime, Dictionary libraryStubs) + { + if (libraries == null) { return Enumerable.Empty(); } - return librariesObject.Properties() - .Select(property => ReadLibrary(property, runtime, libraryStubs)) + return libraries + .Select(property => CreateLibrary(property, runtime, libraryStubs)) .Where(library => library != null); } - private Library ReadLibrary(JProperty property, bool runtime, Dictionary libraryStubs) + private Library CreateLibrary(TargetLibrary targetLibrary, bool runtime, Dictionary libraryStubs) { - var nameWithVersion = property.Name; + var nameWithVersion = targetLibrary.Name; LibraryStub stub; if (!libraryStubs.TryGetValue(nameWithVersion, out stub)) @@ -198,68 +572,57 @@ namespace Microsoft.Extensions.DependencyModel var name = Pool(nameWithVersion.Substring(0, seperatorPosition)); var version = Pool(nameWithVersion.Substring(seperatorPosition + 1)); - var libraryObject = (JObject)property.Value; - - var dependencies = ReadDependencies(libraryObject); - if (runtime) { // Runtime section of this library was trimmed by type:platform - var isCompilationOnly = libraryObject.Value(DependencyContextStrings.CompilationOnlyPropertyName); + var isCompilationOnly = targetLibrary.CompileOnly; if (isCompilationOnly == true) { return null; } - var runtimeTargetsObject = (JObject)libraryObject[DependencyContextStrings.RuntimeTargetsPropertyName]; - - var entries = ReadRuntimeTargetEntries(runtimeTargetsObject).ToArray(); - var runtimeAssemblyGroups = new List(); var nativeLibraryGroups = new List(); - foreach (var ridGroup in entries.GroupBy(e => e.Rid)) + if (targetLibrary.RuntimeTargets != null) { - var groupRuntimeAssemblies = ridGroup - .Where(e => e.Type == DependencyContextStrings.RuntimeAssetType) - .Select(e => e.Path) - .ToArray(); - - if (groupRuntimeAssemblies.Any()) + foreach (var ridGroup in targetLibrary.RuntimeTargets.GroupBy(e => e.Rid)) { - runtimeAssemblyGroups.Add(new RuntimeAssetGroup( - ridGroup.Key, - groupRuntimeAssemblies.Where(a => Path.GetFileName(a) != "_._"))); - } + var groupRuntimeAssemblies = ridGroup + .Where(e => e.Type == DependencyContextStrings.RuntimeAssetType) + .Select(e => e.Path) + .ToArray(); - var groupNativeLibraries = ridGroup - .Where(e => e.Type == DependencyContextStrings.NativeAssetType) - .Select(e => e.Path) - .ToArray(); + if (groupRuntimeAssemblies.Any()) + { + runtimeAssemblyGroups.Add(new RuntimeAssetGroup( + ridGroup.Key, + groupRuntimeAssemblies.Where(a => Path.GetFileName(a) != "_._"))); + } - if (groupNativeLibraries.Any()) - { - nativeLibraryGroups.Add(new RuntimeAssetGroup( - ridGroup.Key, - groupNativeLibraries.Where(a => Path.GetFileName(a) != "_._"))); + var groupNativeLibraries = ridGroup + .Where(e => e.Type == DependencyContextStrings.NativeAssetType) + .Select(e => e.Path) + .ToArray(); + + if (groupNativeLibraries.Any()) + { + nativeLibraryGroups.Add(new RuntimeAssetGroup( + ridGroup.Key, + groupNativeLibraries.Where(a => Path.GetFileName(a) != "_._"))); + } } } - var runtimeAssemblies = ReadAssetList(libraryObject, DependencyContextStrings.RuntimeAssembliesKey) - .ToArray(); - if (runtimeAssemblies.Any()) + if (targetLibrary.Runtimes != null && targetLibrary.Runtimes.Count > 0) { - runtimeAssemblyGroups.Add(new RuntimeAssetGroup(string.Empty, runtimeAssemblies)); + runtimeAssemblyGroups.Add(new RuntimeAssetGroup(string.Empty, targetLibrary.Runtimes)); } - var nativeLibraries = ReadAssetList(libraryObject, DependencyContextStrings.NativeLibrariesKey) - .ToArray(); - if(nativeLibraries.Any()) + if (targetLibrary.Natives != null && targetLibrary.Natives.Count > 0) { - nativeLibraryGroups.Add(new RuntimeAssetGroup(string.Empty, nativeLibraries)); + nativeLibraryGroups.Add(new RuntimeAssetGroup(string.Empty, targetLibrary.Natives)); } - var resourceAssemblies = ReadResourceAssemblies((JObject)libraryObject[DependencyContextStrings.ResourceAssembliesPropertyName]); - return new RuntimeLibrary( type: stub.Type, name: name, @@ -267,96 +630,17 @@ namespace Microsoft.Extensions.DependencyModel hash: stub.Hash, runtimeAssemblyGroups: runtimeAssemblyGroups, nativeLibraryGroups: nativeLibraryGroups, - resourceAssemblies: resourceAssemblies, - dependencies: dependencies, + resourceAssemblies: targetLibrary.Resources ?? Enumerable.Empty(), + dependencies: targetLibrary.Dependencies, serviceable: stub.Serviceable); } else { - var assemblies = ReadAssetList(libraryObject, DependencyContextStrings.CompileTimeAssembliesKey); - return new CompilationLibrary(stub.Type, name, version, stub.Hash, assemblies, dependencies, stub.Serviceable); + var assemblies = (targetLibrary.Compilations != null) ? targetLibrary.Compilations : Enumerable.Empty(); + return new CompilationLibrary(stub.Type, name, version, stub.Hash, assemblies, targetLibrary.Dependencies, stub.Serviceable); } } - private IEnumerable ReadResourceAssemblies(JObject resourcesObject) - { - if (resourcesObject == null) - { - yield break; - } - foreach (var resourceProperty in resourcesObject) - { - yield return new ResourceAssembly( - locale: Pool(resourceProperty.Value[DependencyContextStrings.LocalePropertyName]?.Value()), - path: resourceProperty.Key - ); - } - } - - private IEnumerable ReadRuntimeTargetEntries(JObject runtimeTargetObject) - { - if (runtimeTargetObject == null) - { - yield break; - } - foreach (var libraryProperty in runtimeTargetObject) - { - var libraryObject = (JObject)libraryProperty.Value; - yield return new RuntimeTargetEntryStub() - { - Path = libraryProperty.Key, - Rid = Pool(libraryObject[DependencyContextStrings.RidPropertyName].Value()), - Type = Pool(libraryObject[DependencyContextStrings.AssetTypePropertyName].Value()) - }; - } - } - - private static string[] ReadAssetList(JObject libraryObject, string name) - { - var assembliesObject = (JObject)libraryObject[name]; - - if (assembliesObject == null) - { - return new string[] { }; - } - - return assembliesObject.Properties().Select(property => property.Name).ToArray(); - } - - private Dependency[] ReadDependencies(JObject libraryObject) - { - var dependenciesObject = (JObject)libraryObject[DependencyContextStrings.DependenciesPropertyName]; - - if (dependenciesObject == null) - { - return new Dependency[] { }; - } - - return dependenciesObject.Properties() - .Select(property => new Dependency(Pool(property.Name), Pool((string)property.Value))).ToArray(); - } - - private Dictionary ReadLibraryStubs(JObject librariesObject) - { - var libraries = new Dictionary(); - if (librariesObject != null) - { - foreach (var libraryProperty in librariesObject) - { - var value = (JObject)libraryProperty.Value; - var stub = new LibraryStub - { - Name = Pool(libraryProperty.Key), - Hash = value[DependencyContextStrings.Sha512PropertyName]?.Value(), - Type = Pool(value[DependencyContextStrings.TypePropertyName].Value()), - Serviceable = value[DependencyContextStrings.ServiceablePropertyName]?.Value() == true - }; - libraries.Add(stub.Name, stub); - } - } - return libraries; - } - private string Pool(string s) { if (s == null) @@ -373,17 +657,43 @@ namespace Microsoft.Extensions.DependencyModel return result; } + private class Target + { + public string Name; + + public IEnumerable Libraries; + } + + private struct TargetLibrary + { + public string Name; + + public IEnumerable Dependencies; + + public List Runtimes; + + public List Natives; + + public List Compilations; + + public List RuntimeTargets; + + public List Resources; + + public bool? CompileOnly; + } + private struct RuntimeTargetEntryStub { public string Type; + public string Path; + public string Rid; } private struct LibraryStub { - public string Name; - public string Hash; public string Type; diff --git a/src/Microsoft.Extensions.DependencyModel/JsonTextReaderExtensions.cs b/src/Microsoft.Extensions.DependencyModel/JsonTextReaderExtensions.cs new file mode 100644 index 000000000..bf2122f03 --- /dev/null +++ b/src/Microsoft.Extensions.DependencyModel/JsonTextReaderExtensions.cs @@ -0,0 +1,77 @@ +// 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; + value = reader.ReadAsString(); + 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/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs index 310042d82..6d807ab78 100644 --- a/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs +++ b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs @@ -26,7 +26,8 @@ namespace Microsoft.Extensions.DependencyModel.Tests var context = Read( @"{ ""runtimeTarget"": { - ""name"":"".NETCoreApp,Version=v1.0/osx.10.10-x64"" + ""name"":"".NETCoreApp,Version=v1.0/osx.10.10-x64"", + ""signature"":""target-signature"" }, ""targets"": { "".NETCoreApp,Version=v1.0/osx.10.10-x64"": {}, @@ -35,6 +36,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests context.Target.IsPortable.Should().BeFalse(); context.Target.Framework.Should().Be(".NETCoreApp,Version=v1.0"); context.Target.Runtime.Should().Be("osx.10.10-x64"); + context.Target.RuntimeSignature.Should().Be("target-signature"); } [Fact] @@ -299,7 +301,9 @@ namespace Microsoft.Extensions.DependencyModel.Tests { var context = Read( @"{ - ""runtimeTarget"": "".NETCoreApp,Version=v1.0"", + ""runtimeTarget"": { + ""name"": "".NETCoreApp,Version=v1.0"" + }, ""targets"": { "".NETCoreApp,Version=v1.0"": { ""System.Banana/1.0.0"": {