mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-25 08:12:40 +01:00
Export extends/implements information in the new libphutil library map
Summary: For `PhutilSymbolLoader` queries which include `setAncestorClass()`, we need the map of which classes/interfaces things extend/implement to issue the query efficiently. Without this map, we need to load //every// class/interface and then do `is_subclass_of()`. This is doable, but not very performant if we don't have php-fpm warmup. There are a few performance-sensitive interfaces where we run queries like this, including some in Arcanist, where we'll never have warmup. This map isn't particularly difficult to generate or maintain, so just include it in symbol generation and in the library map. Also set parallelism with a flag, since it was arbitrarily hard-coded and adding flags is easy. 8 actually seems approximately optimal on my machine at least, though. Test Plan: Ran "phutil_rebuild_map.php", opened library map, got a reasonable extension map. Reviewers: vrana, btrahan Reviewed By: vrana CC: aran Maniphest Tasks: T1103 Differential Revision: https://secure.phabricator.com/D2585
This commit is contained in:
parent
e9a6cd26fc
commit
4eb6b97097
2 changed files with 60 additions and 8 deletions
|
@ -40,6 +40,13 @@ $args->parse(
|
|||
'help' => 'Drop the symbol cache and rebuild the entire map from '.
|
||||
'scratch.',
|
||||
),
|
||||
array(
|
||||
'name' => 'limit',
|
||||
'param' => 'N',
|
||||
'default' => 8,
|
||||
'help' => 'Controls the number of symbol mapper subprocesses run '.
|
||||
'at once. Defaults to 8.',
|
||||
),
|
||||
array(
|
||||
'name' => 'root',
|
||||
'wildcard' => true,
|
||||
|
@ -55,6 +62,7 @@ $root = Filesystem::resolvePath(head($root));
|
|||
$builder = new PhutilLibraryMapBuilder($root);
|
||||
|
||||
$builder->setQuiet($args->getArg('quiet'));
|
||||
$builder->setSubprocessLimit($args->getArg('limit'));
|
||||
|
||||
if ($args->getArg('drop-cache')) {
|
||||
$builder->dropSymbolCache();
|
||||
|
@ -80,6 +88,7 @@ final class PhutilLibraryMapBuilder {
|
|||
|
||||
private $root;
|
||||
private $quiet;
|
||||
private $subprocessLimit = 8;
|
||||
|
||||
const LIBRARY_MAP_VERSION_KEY = '__library_version__';
|
||||
const LIBRARY_MAP_VERSION = 2;
|
||||
|
@ -107,7 +116,7 @@ final class PhutilLibraryMapBuilder {
|
|||
* Control status output. Use --quiet to set this.
|
||||
*
|
||||
* @param bool If true, don't show status output.
|
||||
* @return this
|
||||
* @return this
|
||||
*
|
||||
* @task map
|
||||
*/
|
||||
|
@ -117,6 +126,20 @@ final class PhutilLibraryMapBuilder {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Control subprocess parallelism limit. Use --limit to set this.
|
||||
*
|
||||
* @param int Maximum number of subprocesses to run in parallel.
|
||||
* @return this
|
||||
*
|
||||
* @task map
|
||||
*/
|
||||
public function setSubprocessLimit($limit) {
|
||||
$this->subprocessLimit = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build or rebuild the library map.
|
||||
*
|
||||
|
@ -155,8 +178,12 @@ final class PhutilLibraryMapBuilder {
|
|||
|
||||
// Run the analyzer on any files which need analysis.
|
||||
if ($futures) {
|
||||
$this->log("Analyzing ".number_format(count($futures))." files");
|
||||
foreach (Futures($futures)->limit(8) as $file => $future) {
|
||||
$limit = $this->subprocessLimit;
|
||||
$count = number_format(count($futures));
|
||||
|
||||
$this->log("Analyzing {$count} files with {$limit} subprocesses...\n");
|
||||
|
||||
foreach (Futures($futures)->limit($limit) as $file => $future) {
|
||||
$this->log(".");
|
||||
$symbol_map[$file] = $future->resolveJSON();
|
||||
}
|
||||
|
@ -408,7 +435,11 @@ final class PhutilLibraryMapBuilder {
|
|||
* @task source
|
||||
*/
|
||||
private function buildLibraryMap(array $symbol_map) {
|
||||
$library_map = array();
|
||||
$library_map = array(
|
||||
'class' => array(),
|
||||
'function' => array(),
|
||||
'xmap' => array(),
|
||||
);
|
||||
|
||||
// Detect duplicate symbols within the library.
|
||||
foreach ($symbol_map as $file => $info) {
|
||||
|
@ -425,12 +456,21 @@ final class PhutilLibraryMapBuilder {
|
|||
$library_map[$lib_type][$symbol] = $file;
|
||||
}
|
||||
}
|
||||
$library_map['xmap'] += $info['xmap'];
|
||||
}
|
||||
|
||||
// Simplify the common case (one parent) to make the file a little easier
|
||||
// to deal with.
|
||||
foreach ($library_map['xmap'] as $class => $extends) {
|
||||
if (count($extends) == 1) {
|
||||
$library_map['xmap'][$class] = reset($extends);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the map so it is relatively stable across changes.
|
||||
foreach ($library_map as $lib_type => $symbols) {
|
||||
foreach ($library_map as $key => $symbols) {
|
||||
ksort($symbols);
|
||||
$library_map[$lib_type] = $symbols;
|
||||
$library_map[$key] = $symbols;
|
||||
}
|
||||
ksort($library_map);
|
||||
|
||||
|
|
|
@ -118,6 +118,7 @@ foreach ($root->getTokens() as $token) {
|
|||
|
||||
$have = array(); // For symbols we declare.
|
||||
$need = array(); // For symbols we use.
|
||||
$xmap = array(); // For extended classes and implemented interfaces.
|
||||
|
||||
|
||||
// -( Functions )-------------------------------------------------------------
|
||||
|
@ -226,13 +227,16 @@ foreach ($classes as $class) {
|
|||
// This is "class X ... { ... }".
|
||||
$classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
|
||||
foreach ($classes as $class) {
|
||||
$class_name = $class->getChildByIndex(1);
|
||||
$class_name = $class->getChildByIndex(1)->getConcreteString();
|
||||
$extends = $class->getChildByIndex(2);
|
||||
foreach ($extends->selectDescendantsOfType('n_CLASS_NAME') as $parent) {
|
||||
$need[] = array(
|
||||
'type' => 'class',
|
||||
'symbol' => $parent,
|
||||
);
|
||||
|
||||
// Track all 'extends' in the extension map.
|
||||
$xmap[$class_name][] = $parent->getConcreteString();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,6 +303,7 @@ foreach ($interfaces as $interface) {
|
|||
// This is "class X ... { ... }".
|
||||
$classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
|
||||
foreach ($classes as $class) {
|
||||
$class_name = $class->getChildByIndex(1)->getConcreteString();
|
||||
$implements = $class->getChildByIndex(3);
|
||||
$interfaces = $implements->selectDescendantsOfType('n_CLASS_NAME');
|
||||
foreach ($interfaces as $interface) {
|
||||
|
@ -306,6 +311,9 @@ foreach ($classes as $class) {
|
|||
'type' => 'interface',
|
||||
'symbol' => $interface,
|
||||
);
|
||||
|
||||
// Track 'class ... implements' in the extension map.
|
||||
$xmap[$class_name][] = $interface->getConcreteString();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,7 +321,7 @@ foreach ($classes as $class) {
|
|||
// This is "interface X ... { ... }".
|
||||
$interfaces = $root->selectDescendantsOfType('n_INTERFACE_DECLARATION');
|
||||
foreach ($interfaces as $interface) {
|
||||
$interface_name = $interface->getChildByIndex(1);
|
||||
$interface_name = $interface->getChildByIndex(1)->getConcreteString();
|
||||
|
||||
$extends = $interface->getChildByIndex(2);
|
||||
foreach ($extends->selectDescendantsOfType('n_CLASS_NAME') as $parent) {
|
||||
|
@ -321,6 +329,9 @@ foreach ($interfaces as $interface) {
|
|||
'type' => 'interface',
|
||||
'symbol' => $parent,
|
||||
);
|
||||
|
||||
// Track 'interface ... implements' in the extension map.
|
||||
$xmap[$interface_name][] = $parent->getConcreteString();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -367,6 +378,7 @@ foreach ($need as $key => $spec) {
|
|||
$result = array(
|
||||
'have' => $declared_symbols,
|
||||
'need' => $required_symbols,
|
||||
'xmap' => $xmap,
|
||||
);
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue