mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-09-28 21:24:26 +08:00
Using `.eslintignore` file for `clang-format` is removed in crrev.com/c/2091445 but the comment was still there. Bug: 40272289 Change-Id: Iad48d3b7414eb373fdc24917ffac79dd331f08ca Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/5285880 Reviewed-by: Simon Zünd <szuend@chromium.org> Commit-Queue: Simon Zünd <szuend@chromium.org> Auto-Submit: Ergün Erdoğmuş <ergunsh@chromium.org>
233 lines
6.7 KiB
JavaScript
233 lines
6.7 KiB
JavaScript
// Copyright 2021 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// @ts-check
|
|
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import {fileURLToPath} from 'url';
|
|
|
|
/** @type {Map<string, Map<string, string[][]>>} */
|
|
const methods = new Map();
|
|
/** @type {Map<string, string[]>} */
|
|
const includes = new Map();
|
|
|
|
export function clearState() {
|
|
methods.clear();
|
|
includes.clear();
|
|
}
|
|
|
|
export 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);
|
|
}
|
|
|
|
/**
|
|
* @param {WebIDL2.IDLRootType} thing
|
|
* */
|
|
export function walkRoot(thing) {
|
|
switch (thing.type) {
|
|
case 'interface':
|
|
walkInterface(thing);
|
|
break;
|
|
case 'interface mixin':
|
|
case 'namespace':
|
|
walkMembers(thing);
|
|
break;
|
|
case 'includes':
|
|
walkIncludes(thing);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {WebIDL2.IncludesType} thing
|
|
* */
|
|
function walkIncludes(thing) {
|
|
if (includes.has(thing.includes)) {
|
|
includes.get(thing.includes).push(thing.target);
|
|
} else {
|
|
includes.set(thing.includes, [thing.target]);
|
|
}
|
|
}
|
|
/**
|
|
* @param {WebIDL2.InterfaceType} thing
|
|
* */
|
|
function walkInterface(thing) {
|
|
thing.members.forEach(member => {
|
|
switch (member.type) {
|
|
case 'constructor':
|
|
storeMethod('Window', thing.name, member.arguments.map(argName));
|
|
break;
|
|
case 'operation':
|
|
handleOperation(member);
|
|
}
|
|
});
|
|
const namedConstructor = thing.extAttrs.find(extAttr => extAttr.name === 'NamedConstructor');
|
|
if (namedConstructor && namedConstructor.arguments) {
|
|
storeMethod('Window', namedConstructor.rhs.value, namedConstructor.arguments.map(argName));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {WebIDL2.NamespaceType | WebIDL2.InterfaceMixinType} thing
|
|
* */
|
|
function walkMembers(thing) {
|
|
thing.members.forEach(member => {
|
|
if (member.type === 'operation') {
|
|
handleOperation(member);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {WebIDL2.OperationMemberType} member
|
|
* */
|
|
function handleOperation(member) {
|
|
storeMethod(member.parent.name, member.name, member.arguments.map(argName));
|
|
}
|
|
|
|
/**
|
|
* @param {WebIDL2.Argument} a
|
|
* */
|
|
function argName(a) {
|
|
let name = a.name;
|
|
if (a.optional) {
|
|
name = '?' + name;
|
|
}
|
|
if (a.variadic) {
|
|
name = '...' + name;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* @param {string} parent
|
|
* @param {string} name
|
|
* @param {Array<string>} args
|
|
* */
|
|
function storeMethod(parent, name, args) {
|
|
if (!methods.has(name)) {
|
|
methods.set(name, new Map());
|
|
}
|
|
const method = methods.get(name);
|
|
if (!method.has(parent)) {
|
|
method.set(parent, []);
|
|
}
|
|
method.get(parent).push(args);
|
|
}
|
|
|
|
export function postProcess(dryRun = false) {
|
|
for (const name of methods.keys()) {
|
|
// We use the set jsonParents to track the set of different signatures across parent for this function name.
|
|
// If all signatures are identical, we leave out the parent and emit a single NativeFunction entry without receiver.
|
|
const jsonParents = new Set();
|
|
for (const [parent, signatures] of methods.get(name)) {
|
|
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.get(name).set(parent, filteredSignatures);
|
|
jsonParents.add(JSON.stringify(filteredSignatures));
|
|
}
|
|
// If all parents had the same signature for this name, we put a `*` as parent for this entry.
|
|
if (jsonParents.size === 1) {
|
|
methods.set(name, new Map([['*', JSON.parse(jsonParents.values().next().value)]]));
|
|
}
|
|
for (const [parent, signatures] of methods.get(name)) {
|
|
if (signatures.length === 1 && !signatures[0].length) {
|
|
methods.get(name).delete(parent);
|
|
}
|
|
}
|
|
if (methods.get(name).size === 0) {
|
|
methods.delete(name);
|
|
}
|
|
}
|
|
const functions = [];
|
|
for (const [name, method] of methods) {
|
|
if (method.has('*')) {
|
|
// All parents had the same signature so we emit an entry without receiver.
|
|
functions.push({name, signatures: method.get('*')});
|
|
} else {
|
|
const receiversMap = new Map();
|
|
for (const [parent, signatures] of method) {
|
|
const receivers = receiversMap.get(JSON.stringify(signatures)) || new Set();
|
|
if (includes.has(parent)) {
|
|
includes.get(parent).forEach(receiver => receivers.add(receiver));
|
|
} else {
|
|
receivers.add(parent);
|
|
}
|
|
receiversMap.set(JSON.stringify(signatures), receivers);
|
|
}
|
|
for (const [signatures, receivers] of receiversMap) {
|
|
functions.push({name, signatures: JSON.parse(signatures), receivers: Array.from(receivers)});
|
|
}
|
|
}
|
|
}
|
|
const output = `export const NativeFunctions = [\n${
|
|
functions
|
|
.map(
|
|
entry =>
|
|
` {\n${Object.entries(entry).map(kv => ` ${kv[0]}: ${JSON.stringify(kv[1])}`).join(',\n')}\n }`)
|
|
.join(',\n')}\n];`;
|
|
|
|
if (dryRun) {
|
|
return output;
|
|
}
|
|
|
|
fs.writeFileSync(
|
|
(new URL('../../front_end/models/javascript_metadata/NativeFunctions.js', import.meta.url)).pathname,
|
|
`// Copyright 2020 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
// Generated from ${
|
|
path.relative(path.join(fileURLToPath(import.meta.url), '..', '..'), fileURLToPath(import.meta.url))}
|
|
|
|
// clang-format off
|
|
|
|
${output}
|
|
`);
|
|
}
|