blob: b6b5041c9f7eef66592ad9733cbf339615c5d6d6 [file] [log] [blame]
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)}
export const NativeFunctions = ${JSON.stringify(functions)};
/* Legacy exported object */
self.JavaScriptMetadata = self.JavaScriptMetadata || {};
/* Legacy exported object */
JavaScriptMetadata = JavaScriptMetadata || {};
JavaScriptMetadata.NativeFunctions = NativeFunctions;
`);
}