blob: 2dd1ee7b4e88aee6cecb586dc159c3af9828468c [file] [log] [blame]
Joel Einbinder3f23eb22018-05-14 23:27:51 +00001const WebIDL2 = require('webidl2');
2const fs = require('fs');
3const path = require('path');
4const ts = require('typescript');
5const glob = require('glob');
6const methods = {
7 __proto__: null
8};
9const methodsByName = {
10 __proto__: null
11};
12const program =
13 ts.createProgram([path.join(__dirname, 'node_modules', 'typescript', 'lib', 'lib.esnext.d.ts')], {noLib: true});
14for (const file of program.getSourceFiles()) {
15 ts.forEachChild(file, node => {
16 if (node.kind === ts.SyntaxKind.InterfaceDeclaration) {
17 for (const member of node.members) {
18 if (member.kind === ts.SyntaxKind.MethodSignature)
19 parseTSFunction(member, node);
20 }
21 }
22 if (node.kind === ts.SyntaxKind.FunctionDeclaration)
23 parseTSFunction(node, {name: {text: 'Window'}});
24
25 });
26}
27
28function parseTSFunction(func, node) {
29 if (!func.name.escapedText)
30 return;
31
32 const args = func.parameters
33 .map(p => {
34 let text = p.name.escapedText;
35 if (p.questionToken)
36 text = '?' + text;
37 if (p.dotDotDotToken)
38 text = '...' + text;
39 return text;
40 })
41 .filter(x => x !== 'this');
42 storeMethod(node.name.text, func.name.escapedText, args);
43}
44
45const files = glob('../../../+(core|modules)/**/*.idl', {cwd: __dirname}, function(er, files) {
46 for (const file of files) {
47 if (file.includes('testing'))
48 continue;
49 const data = fs.readFileSync(path.join(__dirname, file), 'utf8');
50 const lines = data.split('\n');
51 const newLines = [];
52 for (line of lines) {
53 if (!line.includes(' attribute '))
54 newLines.push(line);
55 }
56
57 try {
58 WebIDL2.parse(newLines.join('\n')).forEach(walk);
59 } catch (e) {
60 // console.error(file);
61 }
62 }
63 WebIDL2
64 .parse(`
65 namespace console {
66 void assert(optional boolean condition = false, any... data);
67 void clear();
68 void count(optional DOMString label = "default");
69 void debug(any... data);
70 void dir(any item, optional object? options);
71 void dirxml(any... data);
72 void error(any... data);
73 void group(any... data);
74 void groupCollapsed(any... data);
75 void groupEnd();
76 void info(any... data);
77 void log(any... data);
78 void profile(optional DOMString title);
79 void profileEnd(optional DOMString title);
80 void table(any... tabularData);
81 void time(optional DOMString label);
82 void timeEnd(optional DOMString label);
83 void timeStamp(optional DOMString name);
84 void trace(any... data);
85 void warn(any... data);
86 };
87`).forEach(walk);
88 postProcess();
89});
90
91function walk(thing, parent) {
92 if (thing.type === 'interface') {
93 const constructor = thing.extAttrs.find(extAttr => extAttr.name === 'Constructor');
94 if (constructor && constructor.arguments && thing.extAttrs.find(extAttr => extAttr.name === 'Exposed'))
95 storeMethod('Window', thing.name, constructor.arguments.map(argName));
96
97 const namedConstructor = thing.extAttrs.find(extAttr => extAttr.name === 'NamedConstructor');
98 if (namedConstructor && namedConstructor.arguments)
99 storeMethod('Window', namedConstructor.rhs.value, namedConstructor.arguments.map(argName));
100 }
101 if (thing.type.includes('operation')) {
102 storeMethod(thing.static ? (parent.name + 'Constructor') : parent.name, thing.name, thing.arguments.map(argName));
103 return;
104 }
105 if (thing.members) {
106 for (const member of thing.members)
107 walk(member, thing);
108 }
109}
110
111function argName(a) {
112 let name = a.name;
113 if (a.optional)
114 name = '?' + name;
115 if (a.variadic)
116 name = '...' + name;
117 return name;
118}
119
120function storeMethod(parent, name, args) {
121 if (!methods[name])
122 methods[name] = {__proto__: null};
123 if (!methods[name][parent])
124 methods[name][parent] = [];
125 methods[name][parent].push(args);
126}
127
128function postProcess() {
129 for (const name in methods) {
130 const jsonParents = new Set();
131 for (const parent in methods[name]) {
132 const signatures = methods[name][parent];
133 signatures.sort((a, b) => a.length - b.length);
134 const filteredSignatures = [];
135 for (const signature of signatures) {
136 const smallerIndex = filteredSignatures.findIndex(smaller => startsThesame(smaller, signature));
137 if (smallerIndex !== -1) {
138 filteredSignatures[smallerIndex] = (signature.map((arg, index) => {
Joel Einbinder705daf02018-05-16 23:57:41 +0000139 const otherArg = filteredSignatures[smallerIndex][index];
140 if (otherArg)
141 return otherArg.length > arg.length ? otherArg : arg;
Joel Einbinder3f23eb22018-05-14 23:27:51 +0000142 if (arg.startsWith('?') || arg.startsWith('...'))
143 return arg;
144 return '?' + arg;
145 }));
146 } else {
147 filteredSignatures.push(signature);
148 }
149 }
150
151 function startsThesame(smaller, bigger) {
152 for (let i = 0; i < smaller.length; i++) {
Joel Einbinder705daf02018-05-16 23:57:41 +0000153 const withoutQuestion = str => /[\?]?(.*)/.exec(str)[1];
154 if (withoutQuestion(smaller[i]) !== withoutQuestion(bigger[i]))
Joel Einbinder3f23eb22018-05-14 23:27:51 +0000155 return false;
156 }
157 return true;
158 }
159
160 methods[name][parent] = filteredSignatures;
161 jsonParents.add(JSON.stringify(filteredSignatures));
162 }
163 if (jsonParents.size === 1) {
164 methods[name] = {'*': JSON.parse(jsonParents.values().next().value)};
165 }
166 for (const parent in methods[name]) {
167 const signatures = methods[name][parent];
168 if (signatures.length === 1 && !signatures[0].length)
169 delete methods[name][parent];
170 }
171 if (!Object.keys(methods[name]).length)
172 delete methods[name];
173 }
174 const functions = [];
175 for (const name in methods) {
176 if (methods[name]['*']) {
177 functions.push({name, signatures: methods[name]['*']});
178 } else {
179 for (const parent in methods[name]) {
180 if (parent.endsWith('Constructor'))
Joel Einbinder75bb4332019-03-05 22:33:55 +0000181 functions.push({name, signatures: methods[name][parent], static: true, receiver: parent.substring(0, parent.length - 'Constructor'.length)});
Joel Einbinder3f23eb22018-05-14 23:27:51 +0000182 else
183 functions.push({name, signatures: methods[name][parent], receiver: parent});
184 }
185 }
186 }
187
188 fs.writeFileSync(
189 path.join(__dirname, '..', '..', 'front_end', 'javascript_metadata', 'NativeFunctions.js'),
190 `// Generated from ${path.relative(path.join(__dirname, '..', '..'), __filename)}
191JavaScriptMetadata.NativeFunctions = ${JSON.stringify(functions)};`);
192}