mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-04 02:51:19 +08:00
Sometimes a function would have multiple signatures in the IDL files with the difference just being some of the arguments are optional. This patch improves the deduplication logic to correctly support array.splice(start, ?deleteCount, ...items) Change-Id: Ie9230d54dadd1c8c7cadbb92afc37fbfa87ea857 Reviewed-on: https://chromium-review.googlesource.com/1062885 Reviewed-by: Andrey Lushnikov <lushnikov@chromium.org> Commit-Queue: Joel Einbinder <einbinder@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#559342} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: ddcb027a039f212e0701f7118ea7dd8b8cea65b4
193 lines
6.2 KiB
JavaScript
193 lines
6.2 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: constructor});
|
|
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)};`);
|
|
}
|