microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
minestarks/circuit-magic

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/npm/qsharp/src/browser.ts

218lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4// This module is the entry point for browser environments. For Node.js environment,
5// the "./main.js" module is the entry point.
6
7import * as wasm from "../lib/web/qsc_wasm.js";
8import initWasm, {
9 IProjectHost,
10 ProjectType,
11 TargetProfile,
12} from "../lib/web/qsc_wasm.js";
13import {
14 Compiler,
15 ICompiler,
16 ICompilerWorker,
17 compilerProtocol,
18} from "./compiler/compiler.js";
19import {
20 IDebugService,
21 IDebugServiceWorker,
22 QSharpDebugService,
23 debugServiceProtocol,
24} from "./debug-service/debug-service.js";
25import { callAndTransformExceptions } from "./diagnostics.js";
26import {
27 ILanguageService,
28 ILanguageServiceWorker,
29 QSharpLanguageService,
30 languageServiceProtocol,
31 qsharpGithubUriScheme,
32 qsharpLibraryUriScheme,
33} from "./language-service/language-service.js";
34import { LogLevel, log } from "./log.js";
35import { ProjectLoader } from "./project.js";
36import { createProxy } from "./workers/browser.js";
37
38export { qsharpGithubUriScheme, qsharpLibraryUriScheme };
39
40// Create once. A module is stateless and can be efficiently passed to WebWorkers.
41let wasmModule: WebAssembly.Module | null = null;
42let wasmModulePromise: Promise<void> | null = null;
43
44// Used to track if an instance is already instantiated
45let wasmInstancePromise: Promise<wasm.InitOutput> | null = null;
46
47async function wasmLoader(uriOrBuffer: string | ArrayBuffer) {
48 if (typeof uriOrBuffer === "string") {
49 log.info("Fetching wasm module from %s", uriOrBuffer);
50 performance.mark("fetch-wasm-start");
51 const wasmRequst = await fetch(uriOrBuffer);
52 const wasmBuffer = await wasmRequst.arrayBuffer();
53 const fetchTiming = performance.measure("fetch-wasm", "fetch-wasm-start");
54 log.logTelemetry({
55 id: "fetch-wasm",
56 data: {
57 duration: fetchTiming.duration,
58 uri: uriOrBuffer,
59 },
60 });
61
62 wasmModule = await WebAssembly.compile(wasmBuffer);
63 } else {
64 log.info("Compiling wasm module from provided buffer");
65 wasmModule = await WebAssembly.compile(uriOrBuffer);
66 }
67}
68
69export function loadWasmModule(
70 uriOrBuffer: string | ArrayBuffer,
71): Promise<void> {
72 // Only initiate if not already in flight, to avoid race conditions
73 if (!wasmModulePromise) {
74 wasmModulePromise = wasmLoader(uriOrBuffer);
75 }
76 return wasmModulePromise;
77}
78
79async function instantiateWasm() {
80 // Ensure loading the module has been initiated, and wait for it.
81 if (!wasmModulePromise) throw "Wasm module must be loaded first";
82 await wasmModulePromise;
83 if (!wasmModule) throw "Wasm module failed to load";
84
85 if (wasmInstancePromise) {
86 // Either in flight or already complete. The prior request will do the init,
87 // so just wait on that.
88 await wasmInstancePromise;
89 return;
90 }
91
92 // Set the promise to signal this is in flight, then wait on the result.
93 wasmInstancePromise = initWasm(wasmModule);
94 await wasmInstancePromise;
95
96 // Once ready, set up logging and telemetry as soon as possible after instantiating
97 wasm.initLogging(log.logWithLevel, log.getLogLevel());
98 log.onLevelChanged = (level) => wasm.setLogLevel(level);
99}
100
101export async function getLibrarySourceContent(
102 path: string,
103): Promise<string | undefined> {
104 await instantiateWasm();
105 return wasm.get_library_source_content(path);
106}
107
108export async function getDebugService(): Promise<IDebugService> {
109 await instantiateWasm();
110 return new QSharpDebugService(wasm);
111}
112
113export async function getProjectLoader(
114 host: IProjectHost,
115): Promise<ProjectLoader> {
116 await instantiateWasm();
117 return new ProjectLoader(wasm, host);
118}
119
120// Create the debugger inside a WebWorker and proxy requests.
121// If the Worker was already created via other means and is ready to receive
122// messages, then the worker may be passed in and it will be initialized.
123export function getDebugServiceWorker(
124 worker: string | Worker,
125): IDebugServiceWorker {
126 if (!wasmModule) throw "Wasm module must be loaded first";
127 return createProxy(worker, wasmModule, debugServiceProtocol);
128}
129
130export async function getCompiler(): Promise<ICompiler> {
131 await instantiateWasm();
132 return new Compiler(wasm);
133}
134
135// Create the compiler inside a WebWorker and proxy requests.
136// If the Worker was already created via other means and is ready to receive
137// messages, then the worker may be passed in and it will be initialized.
138export function getCompilerWorker(worker: string | Worker): ICompilerWorker {
139 if (!wasmModule) throw "Wasm module must be loaded first";
140 return createProxy(worker, wasmModule, compilerProtocol);
141}
142
143export async function getLanguageService(
144 host?: IProjectHost,
145): Promise<ILanguageService> {
146 await instantiateWasm();
147 return new QSharpLanguageService(wasm, host);
148}
149
150// Create the compiler inside a WebWorker and proxy requests.
151// If the Worker was already created via other means and is ready to receive
152// messages, then the worker may be passed in and it will be initialized.
153export function getLanguageServiceWorker(
154 worker: string | Worker,
155): ILanguageServiceWorker {
156 if (!wasmModule) throw "Wasm module must be loaded first";
157 return createProxy(worker, wasmModule, languageServiceProtocol);
158}
159
160/// Extracts the target profile from a Q# source file's entry point.
161/// Scans the provided source code for an EntryPoint argument specifying
162/// a profile and returns the corresponding TargetProfile value, if found.
163/// Returns undefined if no profile is specified or if the profile is not recognized.
164export async function getTargetProfileFromEntryPoint(
165 fileName: string,
166 source: string,
167): Promise<wasm.TargetProfile | undefined> {
168 await instantiateWasm();
169 return callAndTransformExceptions(
170 async () =>
171 wasm.get_target_profile_from_entry_point(fileName, source) as
172 | wasm.TargetProfile
173 | undefined,
174 );
175}
176
177export { StepResultId } from "../lib/web/qsc_wasm.js";
178export type {
179 IBreakpointSpan,
180 ICodeAction,
181 ICodeLens,
182 IDocFile,
183 ILocation,
184 IOperationInfo,
185 IPosition,
186 IProjectConfig,
187 IProjectHost,
188 IQSharpError,
189 IRange,
190 IStackFrame,
191 IStructStepResult,
192 ITestDescriptor,
193 IWorkspaceEdit,
194 VSDiagnostic,
195} from "../lib/web/qsc_wasm.js";
196export { type Dump, type ShotResult } from "./compiler/common.js";
197export { type CompilerState, type ProgramConfig } from "./compiler/compiler.js";
198export { QscEventTarget } from "./compiler/events.js";
199export { QdkDiagnostics } from "./diagnostics.js";
200export type {
201 LanguageServiceDiagnosticEvent,
202 LanguageServiceEvent,
203 LanguageServiceTestCallablesEvent,
204} from "./language-service/language-service.js";
205export { default as openqasm_samples } from "./openqasm-samples.generated.js";
206export type { ProjectLoader } from "./project.js";
207export { default as samples } from "./samples.generated.js";
208export type { CircuitGroup as CircuitData } from "./data-structures/circuit.js";
209export * as utils from "./utils.js";
210export { log, type LogLevel, type ProjectType, type TargetProfile };
211export type {
212 ICompiler,
213 ICompilerWorker,
214 IDebugService,
215 IDebugServiceWorker,
216 ILanguageService,
217 ILanguageServiceWorker,
218};
219