| 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; |
| `); |
| } |