Files
react-native-devtools-frontend/scripts/javascript_natives/index.js
T
Joel Einbinder 75bb433138 DevTools: Fix argument hints for static constructor methods
The constructor name was not being passed correctly into the JSON,
which was causing static methods to be incorrectly parsed as
unique top level functions.

Bug: 913363
Change-Id: If0a94184cceef0c16b8755fe375aad3e2e48de0d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1501495
Reviewed-by: Erik Luo <luoe@chromium.org>
Commit-Queue: Joel Einbinder <einbinder@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#637852}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 02ae595073a7336412d333ff58c4b4b3f144c736
2019-03-05 22:33:55 +00:00

193 lines
6.3 KiB
JavaScript

const WebIDL2 = require('webidl2');
const fs = require('fs');
const path = require('path');
const ts = require('typescript');
const glob = require('glob');
const methods = {
__proto__: null
};
const methodsByName = {
__proto__: null
};
const program =
ts.createProgram([path.join(__dirname, 'node_modules', 'typescript', 'lib', 'lib.esnext.d.ts')], {noLib: true});
for (const file of program.getSourceFiles()) {
ts.forEachChild(file, node => {
if (node.kind === ts.SyntaxKind.InterfaceDeclaration) {
for (const member of node.members) {
if (member.kind === ts.SyntaxKind.MethodSignature)
parseTSFunction(member, node);
}
}
if (node.kind === ts.SyntaxKind.FunctionDeclaration)
parseTSFunction(node, {name: {text: 'Window'}});
});
}
function parseTSFunction(func, node) {
if (!func.name.escapedText)
return;
const args = func.parameters
.map(p => {
let text = p.name.escapedText;
if (p.questionToken)
text = '?' + text;
if (p.dotDotDotToken)
text = '...' + text;
return text;
})
.filter(x => x !== 'this');
storeMethod(node.name.text, func.name.escapedText, args);
}
const files = glob('../../../+(core|modules)/**/*.idl', {cwd: __dirname}, function(er, files) {
for (const file of files) {
if (file.includes('testing'))
continue;
const data = fs.readFileSync(path.join(__dirname, file), 'utf8');
const lines = data.split('\n');
const newLines = [];
for (line of lines) {
if (!line.includes(' attribute '))
newLines.push(line);
}
try {
WebIDL2.parse(newLines.join('\n')).forEach(walk);
} catch (e) {
// console.error(file);
}
}
WebIDL2
.parse(`
namespace console {
void assert(optional boolean condition = false, any... data);
void clear();
void count(optional DOMString label = "default");
void debug(any... data);
void dir(any item, optional object? options);
void dirxml(any... data);
void error(any... data);
void group(any... data);
void groupCollapsed(any... data);
void groupEnd();
void info(any... data);
void log(any... data);
void profile(optional DOMString title);
void profileEnd(optional DOMString title);
void table(any... tabularData);
void time(optional DOMString label);
void timeEnd(optional DOMString label);
void timeStamp(optional DOMString name);
void trace(any... data);
void warn(any... data);
};
`).forEach(walk);
postProcess();
});
function walk(thing, parent) {
if (thing.type === 'interface') {
const constructor = thing.extAttrs.find(extAttr => extAttr.name === 'Constructor');
if (constructor && constructor.arguments && thing.extAttrs.find(extAttr => extAttr.name === 'Exposed'))
storeMethod('Window', thing.name, constructor.arguments.map(argName));
const namedConstructor = thing.extAttrs.find(extAttr => extAttr.name === 'NamedConstructor');
if (namedConstructor && namedConstructor.arguments)
storeMethod('Window', namedConstructor.rhs.value, namedConstructor.arguments.map(argName));
}
if (thing.type.includes('operation')) {
storeMethod(thing.static ? (parent.name + 'Constructor') : parent.name, thing.name, thing.arguments.map(argName));
return;
}
if (thing.members) {
for (const member of thing.members)
walk(member, thing);
}
}
function argName(a) {
let name = a.name;
if (a.optional)
name = '?' + name;
if (a.variadic)
name = '...' + name;
return name;
}
function storeMethod(parent, name, args) {
if (!methods[name])
methods[name] = {__proto__: null};
if (!methods[name][parent])
methods[name][parent] = [];
methods[name][parent].push(args);
}
function postProcess() {
for (const name in methods) {
const jsonParents = new Set();
for (const parent in methods[name]) {
const signatures = methods[name][parent];
signatures.sort((a, b) => a.length - b.length);
const filteredSignatures = [];
for (const signature of signatures) {
const smallerIndex = filteredSignatures.findIndex(smaller => startsThesame(smaller, signature));
if (smallerIndex !== -1) {
filteredSignatures[smallerIndex] = (signature.map((arg, index) => {
const otherArg = filteredSignatures[smallerIndex][index];
if (otherArg)
return otherArg.length > arg.length ? otherArg : arg;
if (arg.startsWith('?') || arg.startsWith('...'))
return arg;
return '?' + arg;
}));
} else {
filteredSignatures.push(signature);
}
}
function startsThesame(smaller, bigger) {
for (let i = 0; i < smaller.length; i++) {
const withoutQuestion = str => /[\?]?(.*)/.exec(str)[1];
if (withoutQuestion(smaller[i]) !== withoutQuestion(bigger[i]))
return false;
}
return true;
}
methods[name][parent] = filteredSignatures;
jsonParents.add(JSON.stringify(filteredSignatures));
}
if (jsonParents.size === 1) {
methods[name] = {'*': JSON.parse(jsonParents.values().next().value)};
}
for (const parent in methods[name]) {
const signatures = methods[name][parent];
if (signatures.length === 1 && !signatures[0].length)
delete methods[name][parent];
}
if (!Object.keys(methods[name]).length)
delete methods[name];
}
const functions = [];
for (const name in methods) {
if (methods[name]['*']) {
functions.push({name, signatures: methods[name]['*']});
} else {
for (const parent in methods[name]) {
if (parent.endsWith('Constructor'))
functions.push({name, signatures: methods[name][parent], static: true, receiver: parent.substring(0, parent.length - 'Constructor'.length)});
else
functions.push({name, signatures: methods[name][parent], receiver: parent});
}
}
}
fs.writeFileSync(
path.join(__dirname, '..', '..', 'front_end', 'javascript_metadata', 'NativeFunctions.js'),
`// Generated from ${path.relative(path.join(__dirname, '..', '..'), __filename)}
JavaScriptMetadata.NativeFunctions = ${JSON.stringify(functions)};`);
}