Skip to content

Commit 196ae54

Browse files
committed
Отправка подлинного объекта в двоичном виде
1 parent 0aa5494 commit 196ae54

5 files changed

Lines changed: 148 additions & 10 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*----------------------------------------------------------
2+
This Source Code Form is subject to the terms of the
3+
Mozilla Public License, v.2.0. If a copy of the MPL
4+
was not distributed with this file, You can obtain one
5+
at http://mozilla.org/MPL/2.0/.
6+
----------------------------------------------------------*/
7+
8+
/*
9+
* Based on https://gist.github.com/NickStrupat/39e659e53a7aa000b737
10+
*/
11+
12+
using System;
13+
using System.Reflection;
14+
using System.Runtime.CompilerServices;
15+
16+
namespace OneScript.DebugProtocol.Internal
17+
{
18+
internal static class AutoPropertyExtensions
19+
{
20+
private const string Prefix = "<";
21+
private const string Suffix = ">k__BackingField";
22+
23+
private static string GetBackingFieldName(string propertyName) => $"{Prefix}{propertyName}{Suffix}";
24+
25+
private static string GetBaseClassPrefix(Type baseClass) => baseClass.Name + "+";
26+
27+
public static FieldInfo GetBackingField(this PropertyInfo propertyInfo)
28+
{
29+
if (propertyInfo == null)
30+
throw new ArgumentNullException(nameof(propertyInfo));
31+
if (!propertyInfo.CanRead || !propertyInfo.GetGetMethod(nonPublic: true)
32+
.IsDefined(typeof(CompilerGeneratedAttribute), inherit: true))
33+
return null;
34+
35+
var backingFieldName = GetBackingFieldName(propertyInfo.Name);
36+
37+
var backingField =
38+
propertyInfo.DeclaringType?.GetField(backingFieldName, BindingFlags.Instance | BindingFlags.NonPublic);
39+
40+
if (backingField == null)
41+
return null;
42+
43+
if (!backingField.IsDefined(typeof(CompilerGeneratedAttribute), inherit: true))
44+
return null;
45+
return backingField;
46+
}
47+
48+
public static string GetFieldSerializationName(this FieldInfo field, PropertyInfo property)
49+
{
50+
if (field.DeclaringType == property.ReflectedType)
51+
{
52+
return field.Name;
53+
}
54+
55+
// This is inherited prop
56+
return GetBaseClassPrefix(field.DeclaringType) + field.Name;
57+
}
58+
}
59+
}

src/OneScript.DebugProtocol/TcpServer/BinaryFormatWriter.cs renamed to src/OneScript.DebugProtocol/Internal/BinaryFormatWriter.cs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ This Source Code Form is subject to the terms of the
77

88
using System;
99
using System.IO;
10+
using System.Linq;
11+
using System.Reflection;
1012
using System.Text;
1113

12-
namespace OneScript.DebugProtocol.TcpServer
14+
namespace OneScript.DebugProtocol.Internal
1315
{
1416
internal class BinaryFormatWriter : IDisposable
1517
{
@@ -22,35 +24,79 @@ public BinaryFormatWriter(Stream target)
2224

2325
public void WriteHeader(int rootId, int headerId)
2426
{
25-
_writer.Write((byte)0);
27+
WriteRecordType(0);
2628
_writer.Write(rootId);
2729
_writer.Write(headerId);
2830
_writer.Write(1);
2931
_writer.Write(0);
3032
}
3133

3234
public void WriteStringRecord(int objectId, string data)
35+
{
36+
WriteRecordType(6);
37+
_writer.Write(objectId);
38+
39+
WriteStringValue(data);
40+
}
41+
42+
private void WriteStringValue(string data)
3343
{
3444
var bytes = Encoding.UTF8.GetBytes(data);
3545
if (bytes.Length > 127)
3646
throw new ArgumentOutOfRangeException(nameof(data), "Length encoding not supported for strings more than 127 bytes");
37-
38-
_writer.Write((byte)6);
39-
_writer.Write(objectId);
47+
4048
_writer.Write((byte)bytes.Length);
4149
_writer.Write(bytes, 0, bytes.Length);
4250
}
4351

52+
public void WriteLibrary(Assembly assembly, int libraryId)
53+
{
54+
WriteRecordType(12);
55+
_writer.Write(libraryId);
56+
WriteStringValue(assembly.GetName().Name);
57+
}
58+
59+
public void WriteClassWithNoFields(Type type, int classId, int libraryId)
60+
{
61+
WriteRecordType(5);
62+
63+
_writer.Write(classId);
64+
WriteStringValue(type.FullName);
65+
_writer.Write(0); // properties count
66+
67+
_writer.Write(libraryId);
68+
}
69+
70+
private void WriteObjectArray(int objectId, object[] arr)
71+
{
72+
if (arr.Length != 0)
73+
throw new ArgumentException("Only empty arrays mapping supported");
74+
75+
WriteRecordType(16);
76+
_writer.Write(objectId);
77+
_writer.Write(0);
78+
}
79+
80+
private void WriteNull()
81+
{
82+
WriteRecordType(10);
83+
}
84+
4485
public void WriteEnd()
4586
{
46-
_writer.Write((byte)11);
87+
WriteRecordType(11);
4788
}
4889

4990
public void Close()
5091
{
5192
Dispose();
5293
}
5394

95+
private void WriteRecordType(byte type)
96+
{
97+
_writer.Write(type);
98+
}
99+
54100
public void Dispose()
55101
{
56102
_writer.Flush();

src/OneScript.DebugProtocol/TcpServer/DefaultMessageServer.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ private void RunCommandsLoop()
8585
{
8686
_serverStopped = true;
8787
}
88+
catch (ThreadInterruptedException)
89+
{
90+
// Сервер принудительно остановлен
91+
_serverStopped = true;
92+
}
8893
catch (Exception e)
8994
{
9095
var eventData = new CommunicationEventArgs
@@ -97,6 +102,8 @@ private void RunCommandsLoop()
97102
OnError?.Invoke(this, eventData);
98103
}
99104
}
105+
106+
_protocolChannel.Dispose();
100107
});
101108

102109
_messageThread.IsBackground = true;
@@ -117,8 +124,8 @@ public void Stop()
117124

118125
if (_messageThread?.IsAlive == true)
119126
{
120-
_protocolChannel.Dispose();
121127
_messageThread.Interrupt();
128+
_protocolChannel.Dispose();
122129
}
123130
}
124131

src/OneScript.DebugProtocol/TcpServer/FormatReconcileUtils.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This Source Code Form is subject to the terms of the
77
using System;
88
using System.IO;
99
using System.Text;
10+
using OneScript.DebugProtocol.Internal;
1011

1112
namespace OneScript.DebugProtocol.TcpServer
1213
{
@@ -19,12 +20,14 @@ public static class FormatReconcileUtils
1920
/// </summary>
2021
public static byte[] GetReconcileMagic()
2122
{
22-
using (var memoryStream = new MemoryStream(128))
23+
using (var memoryStream = new MemoryStream(256))
2324
{
2425
using (var writer = new BinaryFormatWriter(memoryStream))
2526
{
2627
writer.WriteHeader(1, 1);
27-
writer.WriteStringRecord(1, "1C1C1C");
28+
writer.WriteLibrary(typeof(RpcCall).Assembly, 1);
29+
writer.WriteClassWithNoFields(typeof(RpcCall), 1, 1);
30+
//writer.WriteInstance(new object[]{Array.Empty<object>(), "$NonExistentMethod$", nameof(IDebuggerService)});
2831
writer.WriteEnd();
2932
}
3033

src/Tests/VSCode.DebugAdapter.Tests/ReconcileForOlderVersionTests.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.IO;
44
using System.Runtime.Serialization.Formatters.Binary;
55
using FluentAssertions;
6+
using OneScript.DebugProtocol;
67
using OneScript.DebugProtocol.TcpServer;
78
using Xunit;
89

@@ -16,11 +17,33 @@ public void CanReadReconcileViaBinaryFormatter()
1617
using (var stream = new MemoryStream(FormatReconcileUtils.GetReconcileMagic()))
1718
{
1819
var reader = new BinaryFormatter();
20+
1921
var value = reader.Deserialize(stream);
20-
value.Should().Be("1C1C1C");
22+
value.Should().BeEquivalentTo(new RpcCall());
2123
}
2224
}
2325

26+
[Fact(Skip = "Manual run only")]
27+
public void DumpBinaryStream()
28+
{
29+
const string filePath = "<path>";
30+
using (var stream = new MemoryStream(FormatReconcileUtils.GetReconcileMagic()))
31+
{
32+
var serializer = new BinaryFormatter();
33+
var sampleCall = RpcCall.Create(nameof(IDebuggerService), "$NonExistent$");
34+
35+
using (var dest = new MemoryStream())
36+
{
37+
serializer.Serialize(dest, sampleCall);
38+
using (var file = new FileStream(filePath, FileMode.Create))
39+
{
40+
dest.Position = 0;
41+
dest.CopyTo(file);
42+
}
43+
}
44+
}
45+
}
46+
2447
[Fact]
2548
public void ExchangeReconcileVersions()
2649
{

0 commit comments

Comments
 (0)