1f7b8a70eSRiver Riddleimport * as base64 from 'base64-js' 2f7b8a70eSRiver Riddleimport * as vscode from 'vscode' 3f7b8a70eSRiver Riddle 4f7b8a70eSRiver Riddleimport {MLIRContext} from '../mlirContext'; 5f7b8a70eSRiver Riddle 6f7b8a70eSRiver Riddle/** 7f7b8a70eSRiver Riddle * The parameters to the mlir/convert(To|From)Bytecode commands. These 8f7b8a70eSRiver Riddle * parameters are: 9f7b8a70eSRiver Riddle * - `uri`: The URI of the file to convert. 10f7b8a70eSRiver Riddle */ 11f7b8a70eSRiver Riddletype ConvertBytecodeParams = Partial<{uri : string}>; 12f7b8a70eSRiver Riddle 13f7b8a70eSRiver Riddle/** 14f7b8a70eSRiver Riddle * The output of the mlir/convert(To|From)Bytecode commands: 15f7b8a70eSRiver Riddle * - `output`: The output buffer of the command, e.g. a .mlir or bytecode 16f7b8a70eSRiver Riddle * buffer. 17f7b8a70eSRiver Riddle */ 18f7b8a70eSRiver Riddletype ConvertBytecodeResult = Partial<{output : string}>; 19f7b8a70eSRiver Riddle 20f7b8a70eSRiver Riddle/** 21f7b8a70eSRiver Riddle * A custom filesystem that is used to convert MLIR bytecode files to text for 22f7b8a70eSRiver Riddle * use in the editor, but still use bytecode on disk. 23f7b8a70eSRiver Riddle */ 24f7b8a70eSRiver Riddleclass BytecodeFS implements vscode.FileSystemProvider { 25f7b8a70eSRiver Riddle mlirContext: MLIRContext; 26f7b8a70eSRiver Riddle 27f7b8a70eSRiver Riddle constructor(mlirContext: MLIRContext) { this.mlirContext = mlirContext; } 28f7b8a70eSRiver Riddle 29f7b8a70eSRiver Riddle /* 30f7b8a70eSRiver Riddle * Forward to the default filesystem for the various methods that don't need 31f7b8a70eSRiver Riddle * to understand the bytecode <-> text translation. 32f7b8a70eSRiver Riddle */ 33f7b8a70eSRiver Riddle readDirectory(uri: vscode.Uri): Thenable<[ string, vscode.FileType ][]> { 34f7b8a70eSRiver Riddle return vscode.workspace.fs.readDirectory(uri); 35f7b8a70eSRiver Riddle } 36f7b8a70eSRiver Riddle delete(uri: vscode.Uri): void { 37f7b8a70eSRiver Riddle vscode.workspace.fs.delete(uri.with({scheme : "file"})); 38f7b8a70eSRiver Riddle } 39f7b8a70eSRiver Riddle stat(uri: vscode.Uri): Thenable<vscode.FileStat> { 40f7b8a70eSRiver Riddle return vscode.workspace.fs.stat(uri.with({scheme : "file"})); 41f7b8a70eSRiver Riddle } 42f7b8a70eSRiver Riddle rename(oldUri: vscode.Uri, newUri: vscode.Uri, 43f7b8a70eSRiver Riddle options: {overwrite: boolean}): void { 44f7b8a70eSRiver Riddle vscode.workspace.fs.rename(oldUri.with({scheme : "file"}), 45f7b8a70eSRiver Riddle newUri.with({scheme : "file"}), options); 46f7b8a70eSRiver Riddle } 47f7b8a70eSRiver Riddle createDirectory(uri: vscode.Uri): void { 48f7b8a70eSRiver Riddle vscode.workspace.fs.createDirectory(uri.with({scheme : "file"})); 49f7b8a70eSRiver Riddle } 50f7b8a70eSRiver Riddle watch(_uri: vscode.Uri, _options: { 51f7b8a70eSRiver Riddle readonly recursive: boolean; readonly excludes : readonly string[] 52f7b8a70eSRiver Riddle }): vscode.Disposable { 53f7b8a70eSRiver Riddle return new vscode.Disposable(() => {}); 54f7b8a70eSRiver Riddle } 55f7b8a70eSRiver Riddle 56f7b8a70eSRiver Riddle private _emitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>(); 57f7b8a70eSRiver Riddle readonly onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]> = 58f7b8a70eSRiver Riddle this._emitter.event; 59f7b8a70eSRiver Riddle 60f7b8a70eSRiver Riddle /* 61f7b8a70eSRiver Riddle * Read in a bytecode file, converting it to text before returning it to the 62f7b8a70eSRiver Riddle * caller. 63f7b8a70eSRiver Riddle */ 64f7b8a70eSRiver Riddle async readFile(uri: vscode.Uri): Promise<Uint8Array> { 65f7b8a70eSRiver Riddle // Try to start a language client for this file so that we can parse 66f7b8a70eSRiver Riddle // it. 67f7b8a70eSRiver Riddle const client = 68f7b8a70eSRiver Riddle await this.mlirContext.getOrActivateLanguageClient(uri, 'mlir'); 69f7b8a70eSRiver Riddle if (!client) { 70f7b8a70eSRiver Riddle throw new Error( 71f7b8a70eSRiver Riddle 'Failed to activate mlir language server to read bytecode'); 72f7b8a70eSRiver Riddle } 73*82b65496SRiver Riddle 74f7b8a70eSRiver Riddle // Ask the client to do the conversion. 75*82b65496SRiver Riddle let result: ConvertBytecodeResult; 76f7b8a70eSRiver Riddle try { 77*82b65496SRiver Riddle let params: ConvertBytecodeParams = {uri : uri.toString()}; 78*82b65496SRiver Riddle result = await client.sendRequest('mlir/convertFromBytecode', params); 79f7b8a70eSRiver Riddle } catch (e) { 80f7b8a70eSRiver Riddle vscode.window.showErrorMessage(e.message); 81f7b8a70eSRiver Riddle throw new Error(`Failed to read bytecode file: ${e}`); 82f7b8a70eSRiver Riddle } 83*82b65496SRiver Riddle let resultBuffer = new TextEncoder().encode(result.output); 84*82b65496SRiver Riddle 85*82b65496SRiver Riddle // NOTE: VSCode does not allow for extensions to manage files above 50mb. 86*82b65496SRiver Riddle // Detect that here and if our result is too large for us to manage, alert 87*82b65496SRiver Riddle // the user and open it as a new temporary .mlir file. 88*82b65496SRiver Riddle if (resultBuffer.length > (50 * 1024 * 1024)) { 89*82b65496SRiver Riddle const openAsTempInstead: vscode.MessageItem = { 90*82b65496SRiver Riddle title : 'Open as temporary .mlir instead', 91*82b65496SRiver Riddle }; 92*82b65496SRiver Riddle const message: string = `Failed to open bytecode file "${ 93*82b65496SRiver Riddle uri.toString()}". Cannot edit converted bytecode files larger than 50MB.`; 94*82b65496SRiver Riddle const errorResult: vscode.MessageItem|undefined = 95*82b65496SRiver Riddle await vscode.window.showErrorMessage(message, openAsTempInstead); 96*82b65496SRiver Riddle if (errorResult === openAsTempInstead) { 97*82b65496SRiver Riddle let tempFile = await vscode.workspace.openTextDocument({ 98*82b65496SRiver Riddle language : 'mlir', 99*82b65496SRiver Riddle content : result.output, 100*82b65496SRiver Riddle }); 101*82b65496SRiver Riddle await vscode.window.showTextDocument(tempFile); 102*82b65496SRiver Riddle } 103*82b65496SRiver Riddle throw new Error(message); 104*82b65496SRiver Riddle } 105*82b65496SRiver Riddle 106*82b65496SRiver Riddle return resultBuffer; 107f7b8a70eSRiver Riddle } 108f7b8a70eSRiver Riddle 109f7b8a70eSRiver Riddle /* 110f7b8a70eSRiver Riddle * Save the provided content, which contains MLIR text, as bytecode. 111f7b8a70eSRiver Riddle */ 112f7b8a70eSRiver Riddle async writeFile(uri: vscode.Uri, content: Uint8Array, 113f7b8a70eSRiver Riddle _options: {create: boolean, overwrite: boolean}) { 114f7b8a70eSRiver Riddle // Get the language client managing this file. 115f7b8a70eSRiver Riddle let client = this.mlirContext.getLanguageClient(uri, 'mlir'); 116f7b8a70eSRiver Riddle if (!client) { 117f7b8a70eSRiver Riddle throw new Error( 118f7b8a70eSRiver Riddle 'Failed to activate mlir language server to write bytecode'); 119f7b8a70eSRiver Riddle } 120f7b8a70eSRiver Riddle 121f7b8a70eSRiver Riddle // Ask the client to do the conversion. 122f7b8a70eSRiver Riddle let convertParams: ConvertBytecodeParams = { 123f7b8a70eSRiver Riddle uri : uri.toString(), 124f7b8a70eSRiver Riddle }; 125f7b8a70eSRiver Riddle const result: ConvertBytecodeResult = 126f7b8a70eSRiver Riddle await client.sendRequest('mlir/convertToBytecode', convertParams); 127f7b8a70eSRiver Riddle await vscode.workspace.fs.writeFile(uri.with({scheme : "file"}), 128f7b8a70eSRiver Riddle base64.toByteArray(result.output)); 129f7b8a70eSRiver Riddle } 130f7b8a70eSRiver Riddle} 131f7b8a70eSRiver Riddle 132f7b8a70eSRiver Riddle/** 133f7b8a70eSRiver Riddle * A custom bytecode document for use by the custom editor provider below. 134f7b8a70eSRiver Riddle */ 135f7b8a70eSRiver Riddleclass BytecodeDocument implements vscode.CustomDocument { 136f7b8a70eSRiver Riddle readonly uri: vscode.Uri; 137f7b8a70eSRiver Riddle 138f7b8a70eSRiver Riddle constructor(uri: vscode.Uri) { this.uri = uri; } 139f7b8a70eSRiver Riddle dispose(): void {} 140f7b8a70eSRiver Riddle} 141f7b8a70eSRiver Riddle 142f7b8a70eSRiver Riddle/** 143f7b8a70eSRiver Riddle * A custom editor provider for MLIR bytecode that allows for non-binary 144f7b8a70eSRiver Riddle * interpretation. 145f7b8a70eSRiver Riddle */ 146f7b8a70eSRiver Riddleclass BytecodeEditorProvider implements 147f7b8a70eSRiver Riddle vscode.CustomReadonlyEditorProvider<BytecodeDocument> { 148f7b8a70eSRiver Riddle public async openCustomDocument(uri: vscode.Uri, _openContext: any, 149f7b8a70eSRiver Riddle _token: vscode.CancellationToken): 150f7b8a70eSRiver Riddle Promise<BytecodeDocument> { 151f7b8a70eSRiver Riddle return new BytecodeDocument(uri); 152f7b8a70eSRiver Riddle } 153f7b8a70eSRiver Riddle 154f7b8a70eSRiver Riddle public async resolveCustomEditor(document: BytecodeDocument, 155f7b8a70eSRiver Riddle _webviewPanel: vscode.WebviewPanel, 156f7b8a70eSRiver Riddle _token: vscode.CancellationToken): 157f7b8a70eSRiver Riddle Promise<void> { 158f7b8a70eSRiver Riddle // Ask the user for the desired view type. 159f7b8a70eSRiver Riddle const editType = await vscode.window.showQuickPick( 160f7b8a70eSRiver Riddle [ {label : '.mlir', description : "Edit as a .mlir text file"} ], 161f7b8a70eSRiver Riddle {title : 'Select an editor for the bytecode.'}, 162f7b8a70eSRiver Riddle ); 163f7b8a70eSRiver Riddle 164f7b8a70eSRiver Riddle // If we don't have a valid view type, just bail. 165f7b8a70eSRiver Riddle if (!editType) { 166f7b8a70eSRiver Riddle await vscode.commands.executeCommand( 167f7b8a70eSRiver Riddle 'workbench.action.closeActiveEditor'); 168f7b8a70eSRiver Riddle return; 169f7b8a70eSRiver Riddle } 170f7b8a70eSRiver Riddle 171f7b8a70eSRiver Riddle // TODO: We should also provide a non-`.mlir` way of viewing the 172f7b8a70eSRiver Riddle // bytecode, which should also ideally have some support for invalid 173f7b8a70eSRiver Riddle // bytecode files. 174f7b8a70eSRiver Riddle 175f7b8a70eSRiver Riddle // Close the active editor given that we aren't using it. 176f7b8a70eSRiver Riddle await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); 177f7b8a70eSRiver Riddle 178f7b8a70eSRiver Riddle // Display the file using a .mlir format. 179f7b8a70eSRiver Riddle await vscode.window.showTextDocument( 180f7b8a70eSRiver Riddle document.uri.with({scheme : "mlir.bytecode-mlir"}), 181f7b8a70eSRiver Riddle {preview : true, preserveFocus : false}); 182f7b8a70eSRiver Riddle } 183f7b8a70eSRiver Riddle} 184f7b8a70eSRiver Riddle 185f7b8a70eSRiver Riddle/** 186f7b8a70eSRiver Riddle * Register the necessary providers for supporting MLIR bytecode. 187f7b8a70eSRiver Riddle */ 188f7b8a70eSRiver Riddleexport function registerMLIRBytecodeExtensions(context: vscode.ExtensionContext, 189f7b8a70eSRiver Riddle mlirContext: MLIRContext) { 190f7b8a70eSRiver Riddle vscode.workspace.registerFileSystemProvider("mlir.bytecode-mlir", 191f7b8a70eSRiver Riddle new BytecodeFS(mlirContext)); 192f7b8a70eSRiver Riddle vscode.window.registerCustomEditorProvider('mlir.bytecode', 193f7b8a70eSRiver Riddle new BytecodeEditorProvider()); 194f7b8a70eSRiver Riddle} 195