/// <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); }
/// <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); }
/// <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); }
// 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)); }
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)); }