Skip to content

Instantly share code, notes, and snippets.

@kdzwinel
Last active October 25, 2017 17:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kdzwinel/5b27649ec5829121e628b79c276a1e52 to your computer and use it in GitHub Desktop.
Save kdzwinel/5b27649ec5829121e628b79c276a1e52 to your computer and use it in GitHub Desktop.
Quick and dirty implementation of runtime type profiling in Visual Studio Code
const vscode = require('vscode');
const path = require('path');
// puppeteer is great because it's easy to use, but it comes with 30MB headless Chrome that
// we can't use as it doesn't have `Profiler.startTypeProfile` yet. We have to point to a
// locally installed Chrome Canary instead. It's fine for a POC, but puppeteer isn't probably
// a good fit in a long run.
const puppeteer = require('puppeteer');
const PATH_TO_CANARY = '/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary';
let browser = null;
let page = null;
function activate(context) {
const collection = vscode.languages.createDiagnosticCollection('javascript');
const disposable = vscode.commands.registerCommand('extension.typeProfile', async () => {
if (!browser) {
browser = await puppeteer.launch({ executablePath: PATH_TO_CANARY });
page = await browser.newPage();
}
await page.reload();
// I haven't found an official way to send CDP commands directly via puppeteer, so I'm using
// a private API here (remember it's a POC!)
await page._client.send('Runtime.enable');
await page._client.send('Profiler.enable');
await page._client.send('Profiler.startTypeProfile');
const document = vscode.window.activeTextEditor.document;
const fileName = path.basename(document.uri.toString());
// Compile script
const { scriptId, exceptionDetails } = await page._client.send('Runtime.compileScript', {
expression: document.getText(),
sourceURL: fileName,
persistScript: true
});
if (exceptionDetails) {
// Exception lineNumber and columnNumber can be used to highlight offending code.
vscode.window.showErrorMessage(`Error compiling script: ${exceptionDetails.text} ${exceptionDetails.lineNumber}:${exceptionDetails.columnNumber}`);
return;
}
// Execute script
await page._client.send('Runtime.runScript', { scriptId });
const { result } = await page._client.send('Profiler.takeTypeProfile');
const script = result.find(script => script.url === fileName);
if (script) {
const document = vscode.window.activeTextEditor.document;
const diagnostics = script.entries.map(entry => {
// I'm highlighting only 1 character, it'd be better to highlight whole symbol
const typePositionStart = document.positionAt(entry.offset);
const typePositionEnd = new vscode.Position(typePositionStart.line, typePositionStart.character + 1);
const range = new vscode.Range(typePositionStart, typePositionEnd);
const typesString = entry.types.map(type => type.name).join(' or ');
return new vscode.Diagnostic(range, `V8 says it's a ${typesString}`, vscode.DiagnosticSeverity.Information);
});
collection.set(document.uri, diagnostics);
}
await page._client.send('Profiler.stopTypeProfile');
await page._client.send('Profiler.disable');
await page._client.send('Runtime.disable');
});
context.subscriptions.push(disposable);
}
exports.activate = activate;
async function deactivate() {
if (browser) {
await browser.close();
}
}
exports.deactivate = deactivate;
@kdzwinel
Copy link
Author

kdzwinel commented Sep 20, 2017

That's the core of the extension, all other files that are needed to run it can be generated with Yeoman (docs).

You can see it working in this preview.

Some thoughts:

  • my implementation uses browser to execute code, but it could use nodejs (see hinkel/type-profile),
  • running single, self contained file works great, but making file with dependencies work will be much more challenging,
  • ideally this information should be collected while running tests,
  • Chrome Debugging Protocol docs are here.

@nojvek
Copy link

nojvek commented Sep 20, 2017

@kdzwinel, do you reckon this is also available in any of the latest nodejs builds?

@kdzwinel
Copy link
Author

kdzwinel commented Sep 20, 2017

@nojvek yes, you have to use TOT though: https://github.com/v8/node . Also, see https://github.com/fhinkel/type-profile .

[Edit] I've updated the first comment with some more thoughts/info

@auchenberg
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment