Skip to content
This repository has been archived by the owner on Jan 12, 2024. It is now read-only.

Infrastructure for Bond schema versioning #855

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/QsCompiler/BondSchemas/BondSchemas.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<Import Project="..\..\Common\AssemblyCommon.props" />

Expand All @@ -9,7 +9,7 @@
</PropertyGroup>

<ItemGroup>
<BondCodegen Remove="*.bond" />
<BondCodegen Remove="**\*.bond" />
</ItemGroup>

<ItemGroup>
Expand Down
126 changes: 88 additions & 38 deletions src/QsCompiler/BondSchemas/Protocols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Bond;
Expand All @@ -11,6 +13,7 @@

namespace Microsoft.Quantum.QsCompiler.BondSchemas
{
using BondQsCompilation = V2.QsCompilation;
using SimpleBinaryDeserializer = Deserializer<SimpleBinaryReader<InputBuffer>>;
using SimpleBinarySerializer = Serializer<SimpleBinaryWriter<OutputBuffer>>;

Expand All @@ -23,27 +26,36 @@ public static class Protocols
/// Provides thread-safe access to the members and methods of this class.
/// </summary>
private static readonly object BondSharedDataStructuresLock = new object();
private static Task<SimpleBinaryDeserializer>? simpleBinaryDeserializerInitialization = null;
private static Task<SimpleBinarySerializer>? simpleBinarySerializerInitialization = null;
private static readonly IDictionary<Type, Task<SimpleBinaryDeserializer>?> DeserializerInitializations =
new Dictionary<Type, Task<SimpleBinaryDeserializer>?>()
{
#pragma warning disable IDE0001 // Simplify Names
{ typeof(V1.QsCompilation), null },
{ typeof(V2.QsCompilation), null }
#pragma warning restore IDE0001 // Simplify Names
};

private static Task<SimpleBinarySerializer>? serializerInitialization = null;

/// <summary>
/// Deserializes a Q# compilation object from its Bond simple binary representation.
/// </summary>
/// <param name="byteArray">Bond simple binary representation of a Q# compilation object.</param>
/// <remarks>This method waits for <see cref="Task"/>s to complete and may deadlock if invoked through a <see cref="Task"/>.</remarks>
// N.B. Consider adding an options argument to this method to allow for selection of payload to deserialize.
public static SyntaxTree.QsCompilation? DeserializeQsCompilationFromSimpleBinary(
byte[] byteArray)
byte[] byteArray,
Type bondSchemaType)
{
QsCompilation? bondCompilation = null;
object? bondCompilation = null;
var inputBuffer = new InputBuffer(byteArray);
var reader = new SimpleBinaryReader<InputBuffer>(inputBuffer);
lock (BondSharedDataStructuresLock)
{
var deserializer = GetSimpleBinaryDeserializer();
bondCompilation = deserializer.Deserialize<QsCompilation>(reader);
bondCompilation = DeserializeBondSchemaFromSimpleBinary(reader, bondSchemaType);
}

return CompilerObjectTranslator.CreateQsCompilation(bondCompilation);
return Translators.FromBondSchemaToSyntaxTree(bondCompilation);
}

/// <summary>
Expand All @@ -54,15 +66,8 @@ public static void Initialize()
{
lock (BondSharedDataStructuresLock)
{
if (simpleBinaryDeserializerInitialization == null)
{
simpleBinaryDeserializerInitialization = QueueSimpleBinaryDeserializerInitialization();
}

if (simpleBinarySerializerInitialization == null)
{
simpleBinarySerializerInitialization = QueueSimpleBinarySerializerInitialization();
}
TryInitializeDeserializers();
_ = TryInitializeSerializer();
}
}

Expand All @@ -74,10 +79,7 @@ public static void InitializeDeserializer()
{
lock (BondSharedDataStructuresLock)
{
if (simpleBinaryDeserializerInitialization == null)
{
simpleBinaryDeserializerInitialization = QueueSimpleBinaryDeserializerInitialization();
}
TryInitializeDeserializers();
}
}

Expand All @@ -89,10 +91,7 @@ public static void InitializeSerializer()
{
lock (BondSharedDataStructuresLock)
{
if (simpleBinarySerializerInitialization == null)
{
simpleBinarySerializerInitialization = QueueSimpleBinarySerializerInitialization();
}
_ = TryInitializeSerializer();
}
}

Expand All @@ -108,7 +107,7 @@ public static void SerializeQsCompilationToSimpleBinary(
{
var outputBuffer = new OutputBuffer();
var writer = new SimpleBinaryWriter<OutputBuffer>(outputBuffer);
var bondCompilation = BondSchemaTranslator.CreateBondCompilation(qsCompilation);
var bondCompilation = Translators.FromSyntaxTreeToBondSchema(qsCompilation);
lock (BondSharedDataStructuresLock)
{
var serializer = GetSimpleBinarySerializer();
Expand All @@ -120,46 +119,97 @@ public static void SerializeQsCompilationToSimpleBinary(
stream.Position = 0;
}

private static SimpleBinaryDeserializer GetSimpleBinaryDeserializer()
private static object DeserializeBondSchemaFromSimpleBinary(
SimpleBinaryReader<InputBuffer> reader,
Type bondSchemaType)
{
VerifyLockAcquired(BondSharedDataStructuresLock);
if (simpleBinaryDeserializerInitialization == null)
var deserializer = GetSimpleBinaryDeserializer(bondSchemaType);
#pragma warning disable IDE0001 // Simplify Names
if (bondSchemaType == typeof(V1.QsCompilation))
{
return deserializer.Deserialize<V1.QsCompilation>(reader);
}
else if (bondSchemaType == typeof(V2.QsCompilation))
{
simpleBinaryDeserializerInitialization = QueueSimpleBinaryDeserializerInitialization();
return deserializer.Deserialize<V2.QsCompilation>(reader);
}
#pragma warning restore IDE0001 // Simplify Names

throw new ArgumentException($"Unknown Bond schema type '{bondSchemaType}'");
}

simpleBinaryDeserializerInitialization.Wait();
return simpleBinaryDeserializerInitialization.Result;
private static SimpleBinaryDeserializer GetSimpleBinaryDeserializer(Type bondSchemaType)
{
VerifyLockAcquired(BondSharedDataStructuresLock);
var deserializerInitialization = TryInitializeDeserializer(bondSchemaType);
deserializerInitialization.Wait();
return deserializerInitialization.Result;
}

private static SimpleBinarySerializer GetSimpleBinarySerializer()
{
VerifyLockAcquired(BondSharedDataStructuresLock);
if (simpleBinarySerializerInitialization == null)
if (serializerInitialization == null)
{
simpleBinarySerializerInitialization = QueueSimpleBinarySerializerInitialization();
serializerInitialization = QueueSimpleBinarySerializerInitialization();
}

simpleBinarySerializerInitialization.Wait();
return simpleBinarySerializerInitialization.Result;
serializerInitialization.Wait();
return serializerInitialization.Result;
}

private static Task<SimpleBinaryDeserializer> QueueSimpleBinaryDeserializerInitialization()
private static Task<SimpleBinaryDeserializer> QueueSimpleBinaryDeserializerInitialization(Type deserializerType)
{
VerifyLockAcquired(BondSharedDataStructuresLock);

// inlineNested is false in order to decrease the time needed to initialize the deserializer.
// While this setting may also increase deserialization time, we did not notice any performance drawbacks with our Bond schemas.
return Task.Run(() => new SimpleBinaryDeserializer(
type: typeof(QsCompilation),
type: deserializerType,
factory: (Factory?)null,
inlineNested: false));
}

private static Task<SimpleBinarySerializer> QueueSimpleBinarySerializerInitialization()
{
VerifyLockAcquired(BondSharedDataStructuresLock);
return Task.Run(() => new SimpleBinarySerializer(typeof(QsCompilation)));
return Task.Run(() => new SimpleBinarySerializer(typeof(BondQsCompilation)));
}

private static Task<SimpleBinaryDeserializer> TryInitializeDeserializer(Type bondSchemaType)
{
VerifyLockAcquired(BondSharedDataStructuresLock);
if (!DeserializerInitializations.TryGetValue(bondSchemaType, out var deserializerInitialization))
{
throw new ArgumentException($"Unknown Bond schema type '{bondSchemaType}'");
}

if (deserializerInitialization == null)
{
deserializerInitialization = QueueSimpleBinaryDeserializerInitialization(bondSchemaType);
DeserializerInitializations[bondSchemaType] = deserializerInitialization;
}

return deserializerInitialization;
}

private static void TryInitializeDeserializers()
{
foreach (var bondSchemaType in DeserializerInitializations.Keys.ToList())
{
_ = TryInitializeDeserializer(bondSchemaType);
}
}

private static Task<SimpleBinarySerializer> TryInitializeSerializer()
{
VerifyLockAcquired(BondSharedDataStructuresLock);
if (serializerInitialization == null)
{
serializerInitialization = QueueSimpleBinarySerializerInitialization();
}

return serializerInitialization;
}

private static void VerifyLockAcquired(object lockObject)
Expand Down
33 changes: 33 additions & 0 deletions src/QsCompiler/BondSchemas/Translators.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;

namespace Microsoft.Quantum.QsCompiler.BondSchemas
{
using BondQsCompilation = V2.QsCompilation;

internal static class Translators
{
public static SyntaxTree.QsCompilation FromBondSchemaToSyntaxTree<TBond>(
TBond bondCompilation)
{
switch (bondCompilation)
{
#pragma warning disable IDE0001 // Simplify Names
case V1.QsCompilation bondCompilationV1:
return V1.CompilerObjectTranslator.CreateQsCompilation(bondCompilationV1);

case V2.QsCompilation bondCompilationV2:
return V2.CompilerObjectTranslator.CreateQsCompilation(bondCompilationV2);

default:
throw new ArgumentException($"Unknown Bond schema type '{typeof(TBond)}'");
#pragma warning restore IDE0001 // Simplify Names
}
}

public static BondQsCompilation FromSyntaxTreeToBondSchema(SyntaxTree.QsCompilation qsCompilation) =>
V2.BondSchemaTranslator.CreateBondCompilation(qsCompilation);
}
}
Loading