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