Skip to content
This repository was archived by the owner on Sep 1, 2023. It is now read-only.

Commit 0b5be45

Browse files
authoredJul 13, 2021
Pass through to facts (#72)
* Documentation update Remove implied comparisons to composer autoloading. Remove statement about global namespace fallback. Document interactions with native autoloaders. Document HH\Facts mode (not present in this commit). * [Untested] Emit an HH\Facts autoload map May still need to lowercase the names. Will check in a Facts-enabled docker container. * Lowercase the names and call the right functions * Parens * Escape `HH\Facts`, no need for a random form feed * Rename useFactsIfAvailableAndDoNotEmitAStaticMap Now named useFactsIfAvailable * Support hhvm 4.109 and up Hhvm 4.116 and below need a HH_FIXME. Hhvm 4.117 has the hhi and the code typechecks on 4.117. * Update HH_FIXME messages The ext-facts hhi didn't make the cut for 4.117.
1 parent 73f576f commit 0b5be45

File tree

7 files changed

+103
-26
lines changed

7 files changed

+103
-26
lines changed
 

‎.github/workflows/build-and-test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
matrix:
1414
os: [ ubuntu , macos ]
1515
hhvm:
16-
- '4.67'
16+
- '4.109'
1717
- latest
1818
- nightly
1919
runs-on: ${{matrix.os}}-latest

‎README.md

+19-5
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ The autoloader for autoloading classes, enums, functions, typedefs, and constant
55
Usage
66
=====
77

8-
1. Add an `hh_autoload.json` file (see section below) and optionally remove your configuration from composer.json
8+
1. Add an `hh_autoload.json` file (see section below)
99
2. `composer require hhvm/hhvm-autoload`
10-
3. Replace any references to `vendor/autoload.php` with `vendor/autoload.hack` and call `Facebook\AutoloadMap\initialize()`
11-
4. To re-generate the map, run `vendor/bin/hh-autoload`, `composer dump-autoload`, or any other command that generates the map
10+
3. Require the autoload file from your entrypoint functions using `require_once (__DIR__ . '(/..)(repeat as needed)/vendor/autoload.hack');`
11+
4. Call `Facebook\AutolaodMap\initialize()` to register the autoloader with hhvm.
12+
5. To re-generate the map, run `vendor/bin/hh-autoload`, `composer dump-autoload`, or any other command that generates the map
1213

1314
Configuration (`hh_autoload.json`)
1415
==================================
@@ -32,9 +33,10 @@ The following settings are optional:
3233
- `"includeVendor": false` - do not include `vendor/` definitions in `vendor/autoload.hack`
3334
- `"devRoots": [ "path/", ...]` - additional roots to only include in dev mode, not when installed as a dependency.
3435
- `"relativeAutoloadRoot": false` - do not use a path relative to `__DIR__` for autoloading. Instead, use the path to the folder containing `hh_autoload.json` when building the autoload map.
35-
- `"failureHandler:" classname<Facebook\AutoloadMap\FailureHandler>` - use the specified class to handle definitions that aren't the Map. Your handler will not be invoked for functions or constants
36-
that aren't in the autoload map and have the same name as a definition in the global namespace. Defaults to none.
36+
- `"failureHandler:" classname<Facebook\AutoloadMap\FailureHandler>` - use the specified class to handle definitions that aren't the Map. Defaults to none.
3737
- `"devFailureHandler": classname<Facebook\AutoloadMap\FailureHandler>` - use a different handler for development environments. Defaults to the same value as `failureHandler`.
38+
- `"parser:" any of [ext-factparse]"` - select a parser to use, but there is only one valid option. Defaults to a sensible parser.
39+
- `"useFactsIfAvailable": false` - use ext-facts (HH\Facts\...) to back Facebook\AutoloadMap\Generated\map() instead of a codegenned dict. See _Use with HH\Facts_ for more information about this mode.
3840

3941
Use In Development (Failure Handlers)
4042
=====================================
@@ -90,12 +92,24 @@ Information you may need is available from:
9092
- `Facebook\AutoloadMap\Generated\root()`: the directory containing the
9193
project root, i.e. the parent directory of `vendor/`
9294

95+
Use with HH\\Facts
96+
=================
97+
98+
HHVM 4.109 introduced ext-facts and ext-watchman. Unlike the static pre-built autoloader which is built into a [repo authoratative](https://docs.hhvm.com/hhvm/advanced-usage/repo-authoritative) build, this native autoloader works incrementally and is suitable for autoloading in your development environment. For more information about setting up this autoloader, see the [blog post](https://hhvm.com/blog/2021/05/11/hhvm-4.109.html) for hhvm 4.109.
99+
100+
When using a native autoloader (either the repo auth or ext-facts autoloader), you do not need hhvm-autoload to require classes/functions/types/constants at runtime. If you (and your vendor dependencies) do not call any functions in the `Facebook\AutoloadMap` namespace, other than `Facebook\AutoloadMap\initialize()`, you don't need hhvm-autoload anymore. In that case, you could drop this dependency and remove the calls to `initialize()`. If you are using other functions, like `Facebook\AutoloadMap\Generated\map()`, you'd still need the vendor/autoload.hack file that hhvm-autoload generates.
101+
102+
Hhvm-autoload supports outputting a vendor/autoload.hack file which forwards all queries to ext-facts. `Facebook\AutoloadMap\Generated\map_uncached()` will always be up to date in this mode, since `HH\Facts` is always up to date. `Facebook\AutoloadMap\Generated\map()` is memoized (within a request), since some code may rely on getting the same result from multiple calls. You can enable this mode by adding `"useFactsIfAvailable": true` to the hh_autoload.json config file. Hhvm-autoload will emit a shim file instead of a full map. This option is ignored if `HH\Facts\enabled()` returns false, or when `--no-facts` is passed to `vendor/bin/hh-autoload`. We recommend passing `--no-facts` when building for production (specifically repo auth mode). Returning a hardcoded dict is faster than asking `HH\Facts`.
103+
104+
Important to note. Autoloading with a native autoloader does not respect hh_autoload.json. The repo auth autoloader allows any code to use any symbol. The facts autoloader honors the configuration in .hhvmconfig.hdf instead. Make sure that the configuration in hh_autoload.json and .hhvmconfig.hdf match.
105+
93106
How It Works
94107
============
95108

96109
- A parser (FactParse) provides a list of all Hack definitions in the specified locations
97110
- This is used to generate something similar to a classmap, except including other kinds of definitions
98111
- The map is provided to HHVM with [`HH\autoload_set_paths()`](https://docs.hhvm.com/hack/reference/function/HH.autoload_set_paths/)
112+
- If a native autoloader is registered, this autoloader will intentionally not register itself. So calling `Facebook\AutoloadMap\initialize()` in repo auth mode or when the facts based autoloader is registered is a noop.
99113

100114
Contributing
101115
============

‎bin/hh-autoload.hack

+36-14
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use namespace HH\Lib\Vec;
1515
final class GenerateScript {
1616
const type TOptions = shape(
1717
'dev' => bool,
18+
'no-facts' => bool,
1819
);
1920

2021
private static function initBootstrapAutoloader(): void {
@@ -94,22 +95,28 @@ final class GenerateScript {
9495
private static function parseOptions(vec<string> $argv): self::TOptions {
9596
$options = shape(
9697
'dev' => true,
98+
'no-facts' => false,
9799
);
98100
$bin = $argv[0];
99101
$argv = Vec\slice($argv, 1);
100102
foreach ($argv as $arg) {
101-
if ($arg === '--no-dev') {
102-
$options['dev'] = false;
103-
continue;
104-
}
105-
if ($arg === '--help') {
106-
self::printUsage(\STDOUT, $bin);
107-
exit(0);
103+
switch ($arg) {
104+
case '--no-dev':
105+
$options['dev'] = false;
106+
break;
107+
case '--no-facts':
108+
$options['no-facts'] = true;
109+
break;
110+
case '--help':
111+
self::printUsage(\STDOUT, $bin);
112+
exit(0);
113+
default:
114+
\fprintf(\STDERR, "Unrecognized option: '%s'\n", $arg);
115+
self::printUsage(\STDERR, $bin);
116+
exit(1);
108117
}
109-
\fprintf(\STDERR, "Unrecognized option: '%s'\n", $arg);
110-
self::printUsage(\STDERR, $bin);
111-
exit(1);
112118
}
119+
113120
return $options;
114121
}
115122

@@ -139,22 +146,37 @@ final class GenerateScript {
139146
$options['dev'] ? IncludedRoots::DEV_AND_PROD : IncludedRoots::PROD_ONLY,
140147
);
141148

149+
$config = $importer->getConfig();
150+
142151
$handler = $options['dev']
143-
? ($importer->getConfig()['devFailureHandler'] ?? null)
144-
: ($importer->getConfig()['failureHandler'] ?? null);
152+
? $config['devFailureHandler']
153+
: $config['failureHandler'];
154+
155+
$emit_facts_forwarder_file = $config['useFactsIfAvailable'] &&
156+
!$options['no-facts'];
145157

146158
(new Writer())
147159
->setBuilder($importer)
148160
->setRoot(\getcwd())
149-
->setRelativeAutoloadRoot($importer->getConfig()['relativeAutoloadRoot'])
161+
->setRelativeAutoloadRoot($config['relativeAutoloadRoot'])
150162
->setFailureHandler(/* HH_IGNORE_ERROR[4110] */ $handler)
151163
->setIsDev($options['dev'])
164+
->setEmitFactsForwarderFile($emit_facts_forwarder_file)
152165
->writeToDirectory(\getcwd().'/vendor/');
153166
print(\getcwd()."/vendor/autoload.hack\n");
154167
}
155168

156169
private static function printUsage(resource $to, string $bin): void {
157-
\fprintf($to, "USAGE: %s [--no-dev]\n", $bin);
170+
\fprintf(
171+
$to,
172+
"USAGE: %s [--no-dev] [--no-facts]\n".
173+
"See the README for full information.\n".
174+
"The README can be found at:\n\t- %s\n\t- %s.\n",
175+
$bin,
176+
\getcwd().'/vendor/hhvm/hhvm-autoload/README.md',
177+
// ^^^^^^ Not accurate if hhvm-autoload is the top level project.
178+
'https://github.com/hhvm/hhvm-autoload/blob/master/README.md',
179+
);
158180
}
159181

160182
private static function getFileList(string $root): vec<string> {

‎composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
],
1212
"require": {
1313
"composer-plugin-api": "^1.0|^2.0",
14-
"hhvm": "^4.67",
14+
"hhvm": "^4.109",
1515
"hhvm/hsl": "^4.0"
1616
},
1717
"require-dev": {

‎src/Config.hack

+1
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ type Config = shape(
1919
'failureHandler' => ?string,
2020
'devFailureHandler' => ?string,
2121
'relativeAutoloadRoot' => bool,
22+
'useFactsIfAvailable' => bool,
2223
);

‎src/ConfigurationLoader.hack

+10
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@ abstract final class ConfigurationLoader {
8585
'devFailureHandler',
8686
) ??
8787
$failure_handler,
88+
'useFactsIfAvailable' => (
89+
TypeAssert\is_nullable_bool(
90+
$data['useFactsIfAvailable'] ?? null,
91+
'useFactsIfAvailable',
92+
) ??
93+
false
94+
) &&
95+
/* HH_FIXME[2049] Facts landed in 4.109, but the hhi landed in 4.118 */
96+
/* HH_FIXME[4107] Facts landed in 4.109, but the hhi landed in 4.118 */
97+
\HH\Facts\enabled(),
8898
);
8999
}
90100

‎src/Writer.hack

+35-5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ final class Writer {
2626
private bool $relativeAutoloadRoot = true;
2727
private ?string $failureHandler;
2828
private bool $isDev = true;
29+
private bool $emitFactsForwarderFile = false;
2930

3031
/** Mark whether we're running in development mode.
3132
*
@@ -55,6 +56,11 @@ final class Writer {
5556
return $this;
5657
}
5758

59+
public function setEmitFactsForwarderFile(bool $should_forward): this {
60+
$this->emitFactsForwarderFile = $should_forward;
61+
return $this;
62+
}
63+
5864
/** Set the files and maps from a builder.
5965
*
6066
* Convenience function; this is equivalent to calling `setFiles()` and
@@ -166,9 +172,30 @@ final class Writer {
166172
true,
167173
);
168174

169-
$map = \var_export($map, true)
170-
|> \str_replace('array (', 'dict[', $$)
171-
|> \str_replace(')', ']', $$);
175+
if (!$this->emitFactsForwarderFile) {
176+
$memoize = '';
177+
$map_as_string = \var_export($map, true)
178+
|> \str_replace('array (', 'dict[', $$)
179+
|> \str_replace(')', ']', $$);
180+
} else {
181+
$memoize = "\n<<__Memoize>>";
182+
$map_as_string = <<<'EOF'
183+
dict[
184+
/* HH_FIXME[2049] Facts landed in 4.109, but the hhi landed in 4.118 */
185+
/* HH_FIXME[4107] Facts landed in 4.109, but the hhi landed in 4.118 */
186+
'class' => \HH\Lib\Dict\map_keys(\HH\Facts\all_types(), \HH\Lib\Str\lowercase<>),
187+
/* HH_FIXME[2049] Facts landed in 4.109, but the hhi landed in 4.118 */
188+
/* HH_FIXME[4107] Facts landed in 4.109, but the hhi landed in 4.118 */
189+
'function' => \HH\Lib\Dict\map_keys(\HH\Facts\all_functions(), \HH\Lib\Str\lowercase<>),
190+
/* HH_FIXME[2049] Facts landed in 4.109, but the hhi landed in 4.118 */
191+
/* HH_FIXME[4107] Facts landed in 4.109, but the hhi landed in 4.118 */
192+
'type' => \HH\Lib\Dict\map_keys(\HH\Facts\all_type_aliases(), \HH\Lib\Str\lowercase<>),
193+
/* HH_FIXME[2049] Facts landed in 4.109, but the hhi landed in 4.118 */
194+
/* HH_FIXME[4107] Facts landed in 4.109, but the hhi landed in 4.118 */
195+
'constants' => \HH\Facts\all_constants(),
196+
]
197+
EOF;
198+
}
172199

173200
if ($this->relativeAutoloadRoot) {
174201
try {
@@ -206,9 +233,12 @@ function is_dev(): bool {
206233
return (bool) \$override;
207234
}
208235
236+
function map_uncached(): \Facebook\AutoloadMap\AutoloadMap {
237+
return $map_as_string;
238+
}
239+
$memoize
209240
function map(): \Facebook\AutoloadMap\AutoloadMap {
210-
/* HH_IGNORE_ERROR[4110] invalid return type */
211-
return $map;
241+
return map_uncached();
212242
}
213243
214244
} // Generated\

0 commit comments

Comments
 (0)
This repository has been archived.