/// <inheritdoc/>
        public bool Transformation(QsCompilation compilation, out QsCompilation transformed)
        {
            transformed = compilation;
            var generator = new Generator(transformed);

            generator.Apply();

            // write generated QIR to disk
            var assemblyName = this.AssemblyConstants.TryGetValue(ReservedKeywords.AssemblyConstants.AssemblyName, out var asmName) ? asmName : null;
            var targetFile   = Path.GetFullPath(string.IsNullOrWhiteSpace(assemblyName) ? "main.txt" : $"{Path.GetFileName(assemblyName)}.txt");

            PerformanceTracking.TaskStart(PerformanceTracking.Task.BitcodeGeneration);
            var bcOutputFolder = this.AssemblyConstants.TryGetValue(ReservedKeywords.AssemblyConstants.OutputPath, out var path) && !string.IsNullOrWhiteSpace(path) ? path : "qir";
            var bcFile         = CompilationLoader.GeneratedFile(targetFile, Path.GetFullPath(bcOutputFolder), ".bc", "");

            generator.Emit(bcFile, emitBitcode: true);
            PerformanceTracking.TaskEnd(PerformanceTracking.Task.BitcodeGeneration);

            // create the human readable version as well
            var sourceOutputFolder = this.AssemblyConstants.TryGetValue(ReservedKeywords.AssemblyConstants.QirOutputPath, out path) && !string.IsNullOrWhiteSpace(path) ? path : "qir";
            var llvmSourceFile     = CompilationLoader.GeneratedFile(targetFile, Path.GetFullPath(sourceOutputFolder), ".ll", "");

            generator.Emit(llvmSourceFile, emitBitcode: false);
            return(true);
        }
Example #2
0
        /// <summary>
        /// Loads the Q# data structures in a referenced assembly given the Uri to that assembly,
        /// and returns the loaded content as out parameter.
        /// Returns false if some of the content could not be loaded successfully,
        /// possibly because the referenced assembly has been compiled with an older compiler version.
        /// If onDeserializationException is specified, invokes the given action on any exception thrown during deserialization.
        /// Throws the corresponding exceptions if the information cannot be extracted.
        /// </summary>
        /// <exception cref="FileNotFoundException"><paramref name="asm"/> does not exist.</exception>
        /// <exception cref="ArgumentException"><paramref name="asm"/> is not an absolute file URI.</exception>
        public static bool LoadReferencedAssembly(Uri asm, out References.Headers headers, bool ignoreDllResources = false, Action <Exception>?onDeserializationException = null)
        {
            var id = CompilationUnitManager.GetFileId(asm);

            if (!File.Exists(asm.LocalPath))
            {
                throw new FileNotFoundException($"The file '{asm.LocalPath}' given to the assembly loader does not exist.");
            }

            using var stream       = File.OpenRead(asm.LocalPath);
            using var assemblyFile = new PEReader(stream);
            if (ignoreDllResources || !FromResource(assemblyFile, out var compilation, onDeserializationException))
            {
                PerformanceTracking.TaskStart(PerformanceTracking.Task.HeaderAttributesLoading);
                var attributes = LoadHeaderAttributes(assemblyFile);
                PerformanceTracking.TaskEnd(PerformanceTracking.Task.HeaderAttributesLoading);
                PerformanceTracking.TaskStart(PerformanceTracking.Task.ReferenceHeadersCreation);
                headers = new References.Headers(id, attributes);
                PerformanceTracking.TaskEnd(PerformanceTracking.Task.ReferenceHeadersCreation);
                return(ignoreDllResources || !attributes.Any()); // just means we have no references
            }
            PerformanceTracking.TaskStart(PerformanceTracking.Task.ReferenceHeadersCreation);
            headers = new References.Headers(id, compilation?.Namespaces ?? ImmutableArray <QsNamespace> .Empty);
            PerformanceTracking.TaskEnd(PerformanceTracking.Task.ReferenceHeadersCreation);
            return(true);
        }
Example #3
0
        /// <summary>
        /// Given a reader for the byte stream of a dotnet dll, loads any Q# compilation included as a resource.
        /// Returns true as well as the loaded compilation if the given dll includes a suitable resource, and returns false otherwise.
        /// If onDeserializationException is specified, invokes the given action on any exception thrown during deserialization.
        /// May throw an exception if the given binary file has been compiled with a different compiler version.
        /// </summary>
        private static bool FromResource(
            PEReader assemblyFile,
            [NotNullWhen(true)] out QsCompilation?compilation,
            Action <Exception>?onDeserializationException = null)
        {
            compilation = null;
            var              metadataReader              = assemblyFile.GetMetadataReader();
            bool             isBondV1ResourcePresent     = false;
            bool             isNewtonSoftResourcePresent = false;
            ManifestResource resource;

            if (metadataReader.Resources().TryGetValue(DotnetCoreDll.ResourceNameQsDataBondV1, out resource))
            {
                isBondV1ResourcePresent = true;
            }
            else if (metadataReader.Resources().TryGetValue(DotnetCoreDll.ResourceName, out resource))
            {
                isNewtonSoftResourcePresent = true;
            }

            // The offset of resources is relative to the resources directory.
            // It is possible that there is no offset given because a valid dll allows for extenal resources.
            // In all Q# dlls there will be a resource with the specific name chosen by the compiler.
            var isResourcePresent = isBondV1ResourcePresent || isNewtonSoftResourcePresent;
            var resourceDir       = assemblyFile.PEHeaders.CorHeader.ResourcesDirectory;

            if (!assemblyFile.PEHeaders.TryGetDirectoryOffset(resourceDir, out var directoryOffset) ||
                !isResourcePresent ||
                !resource.Implementation.IsNil)
            {
                return(false);
            }

            // This is going to be very slow, as it loads the entire assembly into a managed array, byte by byte.
            // Due to the finite size of the managed array, that imposes a memory limitation of around 4GB.
            // The other alternative would be to have an unsafe block, or to contribute a fix to PEMemoryBlock to expose a ReadOnlySpan.
            PerformanceTracking.TaskStart(PerformanceTracking.Task.LoadDataFromReferenceToStream);
            var image             = assemblyFile.GetEntireImage(); // uses int to denote the length and access parameters
            var absResourceOffset = (int)resource.Offset + directoryOffset;

            // the first four bytes of the resource denote how long the resource is, and are followed by the actual resource data
            var resourceLength = BitConverter.ToInt32(image.GetContent(absResourceOffset, sizeof(int)).ToArray(), 0);
            var resourceData   = image.GetContent(absResourceOffset + sizeof(int), resourceLength).ToArray();

            PerformanceTracking.TaskEnd(PerformanceTracking.Task.LoadDataFromReferenceToStream);

            // Use the correct method depending on the resource.
            if (isBondV1ResourcePresent)
            {
                return(LoadSyntaxTree(resourceData, out compilation, onDeserializationException));
            }
            else if (isNewtonSoftResourcePresent)
            {
                return(LoadSyntaxTree(new MemoryStream(resourceData), out compilation, onDeserializationException));
            }

            return(false);
        }
Example #4
0
        // tools for loading the compiled syntax tree from the dll resource (later setup for shipping Q# libraries)

        /// <summary>
        /// Given a binary representation of compiled Q# code, returns the corresponding Q# compilation.
        /// Returns true if the compilation could be deserialized without throwing an exception, and it is properly instantiated. False otherwise.
        /// If onDeserializationException is specified, invokes the given action on any exception thrown during deserialization.
        /// </summary>
        public static bool LoadSyntaxTree(
            byte[] byteArray,
            [NotNullWhen(true)] out QsCompilation?compilation,
            Action <Exception>?onDeserializationException = null)
        {
            compilation = null;
            try
            {
                PerformanceTracking.TaskStart(PerformanceTracking.Task.SyntaxTreeDeserialization);
                compilation = BondSchemas.Protocols.DeserializeQsCompilationFromSimpleBinary(byteArray);
                PerformanceTracking.TaskEnd(PerformanceTracking.Task.SyntaxTreeDeserialization);
            }
            catch (Exception ex)
            {
                onDeserializationException?.Invoke(ex);
                return(false);
            }

            return(compilation != null && IsValidCompilation(compilation));
        }
Example #5
0
        public static bool LoadSyntaxTree(
            Stream stream,
            [NotNullWhen(true)] out QsCompilation?compilation,
            Action <Exception>?onDeserializationException = null)
        {
            PerformanceTracking.TaskStart(PerformanceTracking.Task.DeserializerInit);
            using var reader = new BsonDataReader(stream);
            PerformanceTracking.TaskEnd(PerformanceTracking.Task.DeserializerInit);
            (compilation, reader.ReadRootValueAsArray) = (null, false);
            try
            {
                PerformanceTracking.TaskStart(PerformanceTracking.Task.SyntaxTreeDeserialization);
                compilation = Json.Serializer.Deserialize <QsCompilation>(reader);
                PerformanceTracking.TaskEnd(PerformanceTracking.Task.SyntaxTreeDeserialization);
            }
            catch (Exception ex)
            {
                onDeserializationException?.Invoke(ex);
                return(false);
            }

            return(compilation != null && IsValidCompilation(compilation));
        }