Skip to content

Commit

Permalink
Cleanup and allocation improvements.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashmind committed Mar 28, 2017
1 parent 4bab2c6 commit 1309514
Show file tree
Hide file tree
Showing 30 changed files with 102 additions and 120 deletions.
2 changes: 2 additions & 0 deletions Common/Internal/Argument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ public static T NotNullAndCast<T>(
/// <returns><paramref name="value"/> if it is greater than or equal to zero.</returns>
public static int PositiveOrZero([NotNull, InvokerParameterName] string name, int value) {
if (value < 0) {
// ReSharper disable once HeapView.BoxingAllocation
throw new ArgumentOutOfRangeException(name, value, "Value must be positive or zero.");
}

Expand All @@ -213,6 +214,7 @@ public static int PositiveOrZero([NotNull, InvokerParameterName] string name, in
/// <returns><paramref name="value"/> if it is greater than zero.</returns>
public static int PositiveNonZero([NotNull, InvokerParameterName] string name, int value) {
if (value <= 0) {
// ReSharper disable once HeapView.BoxingAllocation
throw new ArgumentOutOfRangeException(name, value, "Value must be positive and not zero.");
}

Expand Down
4 changes: 2 additions & 2 deletions Common/Internal/AsyncData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ namespace MirrorSharp.Internal {
internal struct AsyncData {
public static readonly AsyncData Empty = new AsyncData(new ArraySegment<byte>(new byte[0]), false, () => null);

private ArraySegment<byte> _first;
private readonly ArraySegment<byte> _first;
private readonly Func<Task<ArraySegment<byte>?>> _getNextAsync;
private bool _getNextCalled;
private readonly bool _getNextCalled;

public AsyncData(ArraySegment<byte> first, bool mightHaveNext, Func<Task<ArraySegment<byte>?>> getNextAsync) {
_first = first;
Expand Down
11 changes: 11 additions & 0 deletions Common/Internal/CharArrayString.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Immutable;

namespace MirrorSharp.Internal {
internal struct CharArrayString {
public CharArrayString(ImmutableArray<char> chars) {
Chars = chars;
}

public ImmutableArray<char> Chars { get; }
}
}
15 changes: 0 additions & 15 deletions Common/Internal/CharListString.cs

This file was deleted.

12 changes: 9 additions & 3 deletions Common/Internal/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public async Task ReceiveAndProcessAsync(CancellationToken cancellationToken) {
}
}

// ReSharper disable once HeapView.ClosureAllocation
private async Task ReceiveAndProcessInternalAsync(CancellationToken cancellationToken) {
var first = await _socket.ReceiveAsync(new ArraySegment<byte>(_inputBuffer), cancellationToken).ConfigureAwait(false);
if (first.MessageType == WebSocketMessageType.Close) {
Expand All @@ -73,7 +74,7 @@ await handler.ExecuteAsync(
new AsyncData(
new ArraySegment<byte>(_inputBuffer, 1, first.Count - 1),
!first.EndOfMessage,
// can we avoid this allocation?
// Can we avoid this allocation?
async () => {
if (last.EndOfMessage)
return null;
Expand All @@ -86,6 +87,7 @@ await handler.ExecuteAsync(

if (!last.EndOfMessage) {
await ReceiveToEndAsync(cancellationToken).ConfigureAwait(false);
// ReSharper disable once HeapView.BoxingAllocation
throw new InvalidOperationException($"Received message has unread data after command '{(char)commandId}'.");
}

Expand All @@ -99,12 +101,16 @@ private async Task ReceiveToEndAsync(CancellationToken cancellationToken) {

private ICommandHandler ResolveHandler(byte commandId) {
var handlerIndex = commandId - (byte)'A';
if (handlerIndex < 0 || handlerIndex > _handlers.Length - 1)
if (handlerIndex < 0 || handlerIndex > _handlers.Length - 1) {
// ReSharper disable once HeapView.BoxingAllocation
throw new FormatException($"Invalid command: '{(char)commandId}'.");
}

var handler = _handlers[handlerIndex];
if (handler == null)
if (handler == null) {
// ReSharper disable once HeapView.BoxingAllocation
throw new FormatException($"Unknown command: '{(char)commandId}'.");
}
return handler;
}

Expand Down
4 changes: 3 additions & 1 deletion Common/Internal/FastConvert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ public static char Utf8ByteArrayToChar(ArraySegment<byte> bytes) {
CharPool.Return(buffer);
}

if (charCount != 1)
if (charCount != 1) {
// ReSharper disable once HeapView.BoxingAllocation
throw new FormatException($"Expected one char, but conversion produced {charCount}.");
}

return buffer[0];
}
Expand Down
6 changes: 3 additions & 3 deletions Common/Internal/FastUtf8JsonWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void WriteProperty(string name, string value) {
WriteValue(value);
}

public void WriteProperty(string name, CharListString value) {
public void WriteProperty(string name, CharArrayString value) {
WritePropertyName(name);
WriteValue(value);
}
Expand Down Expand Up @@ -107,10 +107,10 @@ public void WriteValue(string value) {
WriteEndValue();
}

public void WriteValue(CharListString value) {
public void WriteValue(CharArrayString value) {
WriteStartValue();
WriteRawByte(Utf8.Quote);
foreach (var @char in value) {
foreach (var @char in value.Chars) {
WriteUnquotedChar(@char);
}
WriteRawByte(Utf8.Quote);
Expand Down
3 changes: 3 additions & 0 deletions Common/Internal/Handlers/SetOptionsHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
using MirrorSharp.Internal.Languages;
using MirrorSharp.Internal.Results;

// ReSharper disable HeapView.DelegateAllocation
// ReSharper disable HeapView.ClosureAllocation

namespace MirrorSharp.Internal.Handlers {
internal class SetOptionsHandler : ICommandHandler {
private static readonly char[] Comma = { ',' };
Expand Down
2 changes: 1 addition & 1 deletion Common/Internal/Handlers/Shared/CompletionSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ private Task SendCompletionListAsync(CompletionList completionList, ICommandResu
var completionSpan = completionList.Span;
var writer = sender.StartJsonMessage("completions");

writer.WriteProperty("commitChars", new CharListString(completionList.Rules.DefaultCommitCharacters));
writer.WriteProperty("commitChars", new CharArrayString(completionList.Rules.DefaultCommitCharacters));
writer.WritePropertyName("span");
writer.WriteSpan(completionSpan);

Expand Down
4 changes: 3 additions & 1 deletion Common/Internal/Handlers/SignatureHelpStateHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ public SignatureHelpStateHandler([NotNull] ISignatureHelpSupport signatureHelp)

public Task ExecuteAsync(AsyncData data, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
var @char = FastConvert.Utf8ByteArrayToChar(data.GetFirst());
if (@char != 'F')
if (@char != 'F') {
// ReSharper disable once HeapView.BoxingAllocation
throw new FormatException($"Unknown SignatureHelp command '{@char}'.");
}

return _signatureHelp.ForceSignatureHelpAsync(session, sender, cancellationToken);
}
Expand Down
23 changes: 13 additions & 10 deletions Common/Internal/Handlers/SlowUpdateHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

namespace MirrorSharp.Internal.Handlers {
internal class SlowUpdateHandler : ICommandHandler {
private static readonly IReadOnlyCollection<CodeAction> NoCodeActions = new CodeAction[0];
[CanBeNull] private readonly ISlowUpdateExtension _extension;

public SlowUpdateHandler([CanBeNull] ISlowUpdateExtension extension) {
Expand All @@ -27,6 +26,8 @@ public SlowUpdateHandler([CanBeNull] ISlowUpdateExtension extension) {

public async Task ExecuteAsync(AsyncData data, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
var compilation = await session.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
// Temporary suppression, need to figure out the best approach here.
// ReSharper disable once HeapView.BoxingAllocation
var diagnostics = (IList<Diagnostic>)await compilation.WithAnalyzers(session.Analyzers).GetAllDiagnosticsAsync(cancellationToken).ConfigureAwait(false);
object extensionResult = null;
if (_extension != null) {
Expand Down Expand Up @@ -55,7 +56,7 @@ private async Task SendSlowUpdateAsync(IReadOnlyList<Diagnostic> diagnostics, Wo
writer.WritePropertyName("span");
writer.WriteSpan(diagnostic.Location.SourceSpan);
var actions = await GetCodeActionsAsync(diagnostic, session, cancellationToken).ConfigureAwait(false);
if (actions.Count > 0) {
if (actions.Length > 0) {
writer.WritePropertyStartArray("actions");
WriteActions(writer, actions, session);
writer.WriteEndArray();
Expand All @@ -71,7 +72,7 @@ private async Task SendSlowUpdateAsync(IReadOnlyList<Diagnostic> diagnostics, Wo
await sender.SendJsonMessageAsync(cancellationToken).ConfigureAwait(false);
}

private static void WriteActions(IFastJsonWriterInternal writer, IReadOnlyCollection<CodeAction> actions, WorkSession session) {
private static void WriteActions(IFastJsonWriterInternal writer, ImmutableArray<CodeAction> actions, WorkSession session) {
foreach (var action in actions) {
if (action is CodeActionWithOptions)
continue;
Expand All @@ -89,22 +90,24 @@ private static void WriteActions(IFastJsonWriterInternal writer, IReadOnlyCollec
}
}

private async Task<IReadOnlyCollection<CodeAction>> GetCodeActionsAsync(Diagnostic diagnostic, WorkSession session, CancellationToken cancellationToken) {
List<CodeAction> actions = null;
private async ValueTask<ImmutableArray<CodeAction>> GetCodeActionsAsync(Diagnostic diagnostic, WorkSession session, CancellationToken cancellationToken) {
// I don't think this can be avoided.
// ReSharper disable once HeapView.ClosureAllocation
ImmutableArray<CodeAction>.Builder actionsBuilder = null;
Action<CodeAction, ImmutableArray<Diagnostic>> registerCodeFix = (action, _) => {
if (actions == null)
actions = new List<CodeAction>();
actions.Add(action);
if (actionsBuilder == null)
actionsBuilder = ImmutableArray.CreateBuilder<CodeAction>();
actionsBuilder.Add(action);
};
var fixContext = new CodeFixContext(session.Document, diagnostic, registerCodeFix, cancellationToken);
var providers = ImmutableDictionary.GetValueOrDefault(session.CodeFixProviders, diagnostic.Id);
if (providers == null)
return NoCodeActions;
return ImmutableArray<CodeAction>.Empty;

foreach (var provider in providers) {
await provider.RegisterCodeFixesAsync(fixContext).ConfigureAwait(false);
}
return actions ?? NoCodeActions;
return actionsBuilder?.ToImmutable() ?? ImmutableArray<CodeAction>.Empty;
}
}
}
4 changes: 2 additions & 2 deletions Common/Internal/IFastJsonWriterInternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace MirrorSharp.Internal {
internal interface IFastJsonWriterInternal : IFastJsonWriter {
void WriteProperty(string name, CharListString value);
void WriteValue(CharListString value);
void WriteProperty(string name, CharArrayString value);
void WriteValue(CharArrayString value);
}
}
4 changes: 3 additions & 1 deletion Common/Internal/Languages/LanguageBase.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
Expand All @@ -19,6 +19,7 @@ protected LanguageBase(
[NotNull] ParseOptions defaultParseOptions,
[NotNull] CompilationOptions defaultCompilationOptions
) {
// ReSharper disable HeapView.BoxingAllocation
Name = name;
HostServices = MefHostServices.Create(new[] {
Assembly.Load(new AssemblyName("Microsoft.CodeAnalysis.Workspaces")),
Expand All @@ -39,6 +40,7 @@ [NotNull] CompilationOptions defaultCompilationOptions
DefaultAnalyzerReferences.SelectMany(r => r.GetAnalyzers(Name))
);
DefaultSignatureHelpProviders = CreateDefaultSignatureHelpProviders();
// ReSharper restore HeapView.BoxingAllocation
}

public string Name { get; }
Expand Down
32 changes: 7 additions & 25 deletions Common/Internal/Reflection/RoslynReflectionFast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis.CodeActions;
Expand All @@ -21,32 +22,13 @@ internal static class RoslynReflectionFast {
RoslynTypes.CodeAction
.GetProperty("NestedCodeActions", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
?.GetMethod.CreateDelegate<Func<CodeAction, ImmutableArray<CodeAction>>>();

public static bool IsInlinable(CodeAction action) => _getIsInlinable(action);
public static ImmutableArray<CodeAction> GetNestedCodeActions(CodeAction action) => _getNestedCodeActions(action);

// Roslyn v1
private static readonly Func<CodeAction, bool> _getIsInvokable =
RoslynTypes.CodeAction
.GetProperty("IsInvokable", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
?.GetMethod.CreateDelegate<Func<CodeAction, bool>>();

private static readonly Func<CodeAction, ImmutableArray<CodeAction>> _getCodeActions =
RoslynTypes.CodeAction
.GetMethod("GetCodeActions", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
?.CreateDelegate<Func<CodeAction, ImmutableArray<CodeAction>>>();

public static bool IsInlinable(CodeAction action) {
if (_getIsInlinable == null) // Roslyn v1
return !_getIsInvokable(action);

return _getIsInlinable(action);
}

public static ImmutableArray<CodeAction> GetNestedCodeActions(CodeAction action) {
if (_getNestedCodeActions == null) // Roslyn v1
return _getCodeActions(action);

return _getNestedCodeActions(action);
}

[SuppressMessage("ReSharper", "HeapView.ClosureAllocation")]
[SuppressMessage("ReSharper", "HeapView.DelegateAllocation")]
[SuppressMessage("ReSharper", "HeapView.ObjectAllocation.Possible")]
public static IEnumerable<Lazy<ISignatureHelpProviderWrapper, OrderableLanguageMetadataData>> GetSignatureHelpProvidersSlow(MefHostServices hostServices) {
var mefHostServicesType = typeof(MefHostServices).GetTypeInfo();
var getExports = EnsureFound(
Expand Down
25 changes: 4 additions & 21 deletions Common/Internal/Reflection/SignatureHelpItemData.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
Expand All @@ -27,33 +28,15 @@ int parameterCount
ParameterCount = parameterCount;
}

// Roslyn v1
[UsedImplicitly] // see FromInternalTypeExpressionSlow
public SignatureHelpItemData(
Func<CancellationToken, IEnumerable<SymbolDisplayPart>> documentationFactory,
ImmutableArray<SymbolDisplayPart> prefixParts,
ImmutableArray<SymbolDisplayPart> separatorParts,
ImmutableArray<SymbolDisplayPart> suffixParts,
IEnumerable<SignatureHelpParameterData> parameters,
int parameterCount
) {
DocumentationFactory = documentationFactory != null
? (Func<CancellationToken, IEnumerable<TaggedText>>)(t => documentationFactory(t).ToTaggedText())
: null;
PrefixDisplayParts = prefixParts.ToTaggedText();
SeparatorDisplayParts = separatorParts.ToTaggedText();
SuffixDisplayParts = suffixParts.ToTaggedText();
Parameters = parameters;
ParameterCount = parameterCount;
}

public Func<CancellationToken, IEnumerable<TaggedText>> DocumentationFactory { get; }
public ImmutableArray<TaggedText> PrefixDisplayParts { get; }
public ImmutableArray<TaggedText> SeparatorDisplayParts { get; }
public ImmutableArray<TaggedText> SuffixDisplayParts { get; }
public IEnumerable<SignatureHelpParameterData> Parameters { get; }
public int ParameterCount { get; }


[SuppressMessage("ReSharper", "HeapView.ClosureAllocation")]
[SuppressMessage("ReSharper", "HeapView.DelegateAllocation")]
public static Expression FromInternalTypeExpressionSlow(Expression expression) {
var displayPartsType = expression.Property("PrefixDisplayParts").Type;
var constructor = typeof(SignatureHelpItemData).GetTypeInfo()
Expand Down
11 changes: 3 additions & 8 deletions Common/Internal/Reflection/SignatureHelpParameterData.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
Expand All @@ -14,18 +15,12 @@ public SignatureHelpParameterData(IList<TaggedText> displayParts, IList<TaggedTe
SuffixDisplayParts = suffixDisplayParts;
}

// Roslyn v1
[UsedImplicitly] // see FromInternalTypeExpressionSlow
public SignatureHelpParameterData(IList<SymbolDisplayPart> displayParts, IList<SymbolDisplayPart> prefixDisplayParts, IList<SymbolDisplayPart> suffixDisplayParts) {
DisplayParts = displayParts.ToTaggedText();
PrefixDisplayParts = prefixDisplayParts.ToTaggedText();
SuffixDisplayParts = suffixDisplayParts.ToTaggedText();
}

public IList<TaggedText> DisplayParts { get; }
public IList<TaggedText> PrefixDisplayParts { get; }
public IList<TaggedText> SuffixDisplayParts { get; }

[SuppressMessage("ReSharper", "HeapView.ClosureAllocation")]
[SuppressMessage("ReSharper", "HeapView.DelegateAllocation")]
public static Expression FromInternalTypeExpressionSlow(Expression expression) {
var displayPartsType = expression.Property("DisplayParts").Type;
var constructor = typeof(SignatureHelpParameterData).GetTypeInfo()
Expand Down
Loading

0 comments on commit 1309514

Please sign in to comment.