-
Notifications
You must be signed in to change notification settings - Fork 217
Update M.E.DependencyModel to use S.T.Json source for netstandard2.0 #5009
Conversation
|
||
namespace Microsoft.Extensions.DependencyModel | ||
{ | ||
internal struct StreamBufferWriter : IBufferWriter<byte>, IDisposable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should consider adding such an adapter between IBufferWriter and Stream, publicly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please. We have something similar in SignalR https://github.com/aspnet/AspNetCore/blob/958e9049894137bb08c0d239dca0cda7c850a691/src/SignalR/common/Shared/MemoryBufferWriter.cs#L14 (ignore the fact that it's also a Stream, we just did that for other reasons).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also the PipeWriter implemenation over a Stream is 90% similar https://github.com/aspnet/AspNetCore/blob/958e9049894137bb08c0d239dca0cda7c850a691/src/Http/Http/src/StreamPipeWriter.cs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is something that @JeremyKuhne might have looked into.
/cc @danmosemsft
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -6,29 +6,41 @@ | |||
<TargetFrameworks>net451;netstandard1.3;netstandard1.6;netstandard2.0</TargetFrameworks> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should we do about the other TFMs?
return false; | ||
} | ||
|
||
internal static void Skip(this ref Utf8JsonReader jsonReader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an issue to add this API: https://github.com/dotnet/corefx/issues/33295
|
||
// Add runtime-specific assets | ||
bool wroteObjectStart = false; | ||
wroteObjectStart = AddRuntimeSpecificAssetGroups(DependencyContextStrings.RuntimeAssetType, runtimeLibrary.RuntimeAssemblyGroups, wroteObjectStart, ref jsonWriter); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was necessary since the writer is forward only (while building a JObject and writing at the end did not care about the order).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably worth a comment in the code then until this moves to the JsonDocument (writer)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we would move this to JsonDocument (as it is written today) especially since this is writing (not reading). I was only suggesting that we needed this bool wroteObjectStart
due to the ordering of the writes being important (while the ordering of adding to JObject
is not really important).
|
||
private const int UnseekableStreamInitialRentSize = 4096; | ||
|
||
private static ArraySegment<byte> ReadToEnd(Stream stream) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Borrowed from https://github.com/dotnet/corefx/blob/d1da417dbf38079f06b397bdf3c8f51b94e62238/src/System.Text.Json/src/System/Text/Json/JsonDocument.Parse.cs#L182. I am working on providing better stream support with the reader, but it still results in the calling code being overly complex. Reading the stream to the end atm for simplicity.
src/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.Netstandard2_0.cs
Outdated
Show resolved
Hide resolved
internal 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}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Utf8JsonReader
doesn't track/create a path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO you should only be using public APIs from the reader.
If position info isn't already public then this is a good argument for providing it. If you need it here then other users of the reader will also likely use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO you should only be using public APIs from the reader.
Agreed.
However, I don't really understand the need for this method. Why not rely on the exceptions thrown by the JsonReader itself? But, you are right that we may want to expose these publicly.
cc @eerhardt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the code was that way since it was migrated from using JObject
to simply the JsonTextReader
.
I assume it is so we can give "good" exception messages about exactly where we expect a new object/array/etc. What is the exception message you get when you rely on the exceptions thrown by the JsonReader itself?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You get a JsonReaderException
with LineNumber and BytePosition included in the message, when reading an invalid JSON.
The message string is generally one of the following:
https://github.com/dotnet/corefx/blob/2d9dd533e16889ea4d279162df4039982bfbbca2/src/System.Text.Json/src/Resources/Strings.resx#L120-L284
See where we throw them:
https://github.com/dotnet/corefx/blob/2d9dd533e16889ea4d279162df4039982bfbbca2/src/System.Text.Json/src/System/Text/Json/ThrowHelper.cs#L231
For example, for invalid JSON, you could see a message like: Expected end of string, but instead reached end of data.
OR as another example:
Message: System.Text.Json.JsonReaderException : '}' is an invalid start of a property name. Expected a '"'. LineNumber: 7 | BytePositionInLine: 4.
Now, assuming that the JSON itself is valid, and we are trying to validate the schema, calling GetString on something other than json string would throw an invalid operation exception with an invalid cast message: Cannot get the value of a token type '{0}' as a {1}.
https://github.com/dotnet/corefx/blob/2d9dd533e16889ea4d279162df4039982bfbbca2/src/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.cs#L26
This exception message wouldn't contain the info about where we expect a new object/array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If position info isn't already public then this is a good argument for providing it. If you need it here then other users of the reader will also likely use it.
src/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.Netstandard2_0.cs
Outdated
Show resolved
Hide resolved
src/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.Netstandard2_0.cs
Outdated
Show resolved
Hide resolved
src/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.Netstandard2_0.cs
Outdated
Show resolved
Hide resolved
|
||
if (runtimeTarget == null) | ||
{ | ||
throw new FormatException("No runtime target found"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm rather surprised that core-setup isn't using resource strings
src/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.Netstandard2_0.cs
Outdated
Show resolved
Hide resolved
src/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.Netstandard2_0.cs
Outdated
Show resolved
Hide resolved
src/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.Netstandard2_0.cs
Outdated
Show resolved
Hide resolved
|
||
namespace Microsoft.Extensions.DependencyModel | ||
{ | ||
internal static class JsonTextReaderExtensions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels like all these methods are things that most consumers would want... and it is obviously a non-trivial amount of code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eerhardt A lot of the code in these extension methods is just about changing the exception type, and combining the move and get operations. If InvalidOperationException were acceptable then ReadAsString()
is just
reader.Next();
return reader.GetStringValue();
The return value of Next is important when using the reader in async/chunked mode; and an exception model is what it is...
src/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.Netstandard2_0.cs
Outdated
Show resolved
Hide resolved
src/managed/Microsoft.Extensions.DependencyModel/DependencyContextWriter.Netstandard2_0.cs
Outdated
Show resolved
Hide resolved
src/managed/Microsoft.Extensions.DependencyModel/Microsoft.Extensions.DependencyModel.csproj
Outdated
Show resolved
Hide resolved
src/managed/Microsoft.Extensions.DependencyModel/StreamBufferWriter.Netstandard2_0.cs
Outdated
Show resolved
Hide resolved
src/managed/Microsoft.Extensions.DependencyModel/ArrayBufferWriter.cs
Outdated
Show resolved
Hide resolved
src/managed/Microsoft.Extensions.DependencyModel/Microsoft.Extensions.DependencyModel.csproj
Outdated
Show resolved
Hide resolved
@@ -30,7 +28,7 @@ public void ReadsRuntimeTargetInfo() | |||
""signature"":""target-signature"" | |||
}, | |||
""targets"": { | |||
"".NETCoreApp,Version=v1.0/osx.10.10-x64"": {}, | |||
"".NETCoreApp,Version=v1.0/osx.10.10-x64"": {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's really weird is that using DependencyModel v2.1
, I tried adding trailing commas and I was getting exceptions being thrown from the current implementation. I'm surprised that these tests are passing.
src/managed/Microsoft.Extensions.DependencyModel/ArrayBufferWriter.cs
Outdated
Show resolved
Hide resolved
src/managed/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.Utf8JsonReader.cs
Outdated
Show resolved
Hide resolved
src/managed/Microsoft.Extensions.DependencyModel/UnifiedJsonReader.Utf8JsonReader.cs
Show resolved
Hide resolved
using UnifiedJsonWriter extract out common code by using UnifiedJsonWriterr
Build failures for Arm/Arm64 on Windows are unrelated: https://dev.azure.com/dnceng/public/_build/results?buildId=81361
cc @dotnet/dnceng |
JsonReaderException.
@ahsonkhan I looked into this in https://github.com/dotnet/core-setup/issues/5052, see notes there. |
<PackageReference Include="Microsoft.Bcl.Json.Sources" Version="4.6.0-preview.19072.2"> | ||
<PrivateAssets>All</PrivateAssets> | ||
</PackageReference> | ||
<PackageReference Include="System.Memory" Version="4.5.2" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need 4.5.2
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As opposed to what? 4.5.2 is the latest version available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but is the latest version the right thing to choose? Typically libraries don't update their dependencies just because there is a new version out. Doing so causes anyone who references you to use the latest version, which may not be the right thing.
Is there a specific reason you chose 4.5.2
? or was it just because it was the latest version?
In reply to: 251573648 [](ancestors = 251573648)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically libraries don't update their dependencies just because there is a new version out. Doing so causes anyone who references you to use the latest version, which may not be the right thing.
Good point. I didn't have a specific need for the latest. I believe I can safely downgrade to 4.5.0
.
src/managed/Microsoft.Extensions.DependencyModel/Microsoft.Extensions.DependencyModel.csproj
Outdated
Show resolved
Hide resolved
src/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs
Show resolved
Hide resolved
…otnet/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 dotnet/core-setup@45f9401
Partially addresses https://github.com/dotnet/core-setup/issues/3311
Initial attempt to port Microsoft.Extensions.DependencyModel to use the System.Text.Json source package for TFM netstandard2.0. Note:
There are some API changes going through so this is still WIP.We have the JsonReader and JsonWriter available atm so only using the components that are ready.The System.Text.Json binary is in-box on .NET Core 3.0.
We can't remove the reference to Newtonsoft since M.E.DependencyModel has multiply TFMs:
net451;netstandard1.3;netstandard1.6;netstandard2.0
Is it possible to drop support for all but netstandard2.0? Otherwise, we have to conditionally include the source package.
See https://github.com/dotnet/corefx/blob/master/src/System.Text.Json/source_package/README.md
Here are the diffs to try to highlight what changed during the port:
Related PR: #5010
cc @joshfree, @bartonjs, @steveharter, @eerhardt, @KrzysztofCwalina, @JamesNK, @glennc