Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
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

This comment has been minimized.

Show comment Hide comment
@kdzwinel

kdzwinel 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.
Owner

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

This comment has been minimized.

Show comment Hide comment
@nojvek

nojvek Sep 20, 2017

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

nojvek commented Sep 20, 2017

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

@kdzwinel

This comment has been minimized.

Show comment Hide comment
@kdzwinel

kdzwinel 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

Owner

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

This comment has been minimized.

Show comment Hide comment
@auchenberg

auchenberg Oct 25, 2017

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