/hphp/tools/docskel/docskel.php
PHP | 307 lines | 265 code | 33 blank | 9 comment | 46 complexity | 86d2a947fb51b0a67115c11960b23fce MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, MIT, LGPL-2.0, Apache-2.0
- <?hh
- include(__DIR__ . '/base.php');
- // see hphp/runtime/vm/bytecode.h:kMaxBuiltinArgs
- const MAX_BUILTIN_ARGS = 5;
- function generateDocComment(string $doccomment, ?array $func = null,
- string $indent = ''): string {
- if ($func === null) {
- $func = [];
- }
- $str = $doccomment . "\n";
- if (!empty($func['args'])) {
- $str .= "\n";
- foreach ($func['args'] as $arg) {
- $desc = isset($arg['desc']) ? $arg['desc'] : '';
- $v = "@param {$arg['type']} \${$arg['name']} - " .
- str_replace("\n", " ", $desc);
- $str .= implode("\n ", explode("\n",
- wordwrap($v, 70 - strlen($indent)))) . "\n";
- }
- }
- if (!empty($func['return'])) {
- $str .= "\n";
- $v = "@return {$func['return']['type']} - ";
- $v .= isset($func['return']['desc'])
- ? str_replace("\n", " ", $func['return']['desc']) . "\n"
- : '';
- $str .= implode("\n ", explode("\n",
- wordwrap($v, 70 - strlen($indent)))) . "\n";
- }
- $str = trim($str);
- if (empty($str)) return '';
- $block = wordwrap(trim($str), 75 - strlen($indent));
- return "$indent/**\n".
- "$indent * ".
- str_replace(["\n"," \n"], ["\n$indent * ","\n"], $block).
- "\n$indent */\n";
- }
- function generateFunctionSignature(array $func, string $indent = ''): string {
- $modifiers = empty($func['modifiers'])
- ? '' : (implode(' ', $func['modifiers']) . ' ');
- $ret = "$indent{$modifiers}function {$func['name']}(";
- $argspace = ",\n" . str_repeat(' ', strlen($ret));
- $notfirst = false;
- foreach ($func['args'] as $arg) {
- if ($notfirst) $ret .= $argspace;
- $notfirst = true;
- if (empty($arg['type'])) {
- $ret .= "mixed";
- } else {
- $ret .= $arg['type'];
- }
- $ret .= ' ';
- if ($arg['reference']) {
- $ret .= '&';
- }
- $ret .= "\${$arg['name']}";
- if (!empty($arg['default'])) {
- $ret .= " = {$arg['default']}";
- }
- }
- $ret .= '): ' . (empty($func['return']['type']) ? 'void'
- : $func['return']['type']);
- $annotation = count($func['args']) > MAX_BUILTIN_ARGS
- ? '<<__Native("ActRec")>>' : '<<__Native>>';
- return "$indent$annotation\n$ret;\n\n";
- }
- function outputSystemlib(string $dest, array $funcs, array $classes):void {
- $fp = fopen($dest, 'w');
- fwrite($fp, "<?hh\n");
- fwrite($fp, "// @"."generated by docskel.php\n\n");
- foreach($classes as $class) {
- if (!empty($class['intro'])) {
- fwrite($fp, generateDocComment($class['intro']));
- }
- fwrite($fp, "class {$class['name']}");
- if (!empty($class['extends'])) {
- fwrite($fp, " extends {$class['extends']}");
- }
- if (!empty($class['implements'])) {
- fwrite($fp, " implements " . implode(', ', $class['implements']));
- }
- fwrite($fp, " {\n");
- if (!empty($class['functions'])) {
- foreach($class['functions'] as $func) {
- fwrite($fp, generateDocComment($func['desc'], $func, ' '));
- fwrite($fp, generateFunctionSignature($func, ' '));
- }
- }
- fwrite($fp, "}\n\n");
- }
- foreach($funcs as $func) {
- fwrite($fp, generateDocComment($func['desc'], $func));
- fwrite($fp, generateFunctionSignature($func));
- }
- }
- function getMethod(string $name, array $classes): array {
- $colon = strpos($name, '::');
- if ($colon === false) {
- return array();
- }
- $cname = strtolower(substr($name, 0, $colon));
- $mname = strtolower(substr($name, $colon + 2));
- if (isset($classes[$cname]['functions'][$mname])) {
- return $classes[$cname]['functions'][$mname];
- }
- return array();
- }
- function generateCPPStub(array $func, array $classes): string {
- static $typemap = [
- // type => [return, param, actrec]
- 'bool' => ['bool', 'bool', 'KindOfBoolean'],
- 'int' => ['int64_t', 'int64_t', 'KindOfInt64'],
- 'float' => ['double', 'double', 'KindOfDouble'],
- 'string' => ['String', 'const String&', 'KindOfString'],
- 'array' => ['Array', 'const Array&', 'KindOfArray'],
- 'object' => ['Object', 'const Object&', 'KindOfObject'],
- 'resource' => ['Resource', 'const Resource&', 'KindOfResource'],
- 'mixed' => ['Variant', 'const Variant&', 'KindOfAny'],
- 'void' => ['void'],
- 'reference' => ['Variant', 'VRefParam', 'KindOfRef'],
- ];
- $actrec = count($func['args']) > MAX_BUILTIN_ARGS;
- $alias = !empty($func['class']) || empty($func['alias'])
- ? null : getMethod($func['alias'], $classes);
- // return type
- $ret = 'static ';
- if ($actrec) {
- $ret .= 'TypedValue*';
- } else if (empty($func['return']['type'])) {
- $ret .= 'void';
- } else if (isset($typemap[$func['return']['type']])) {
- $ret .= $typemap[$func['return']['type']][0];
- } else {
- $ret .= 'Object';
- }
- // function name
- if (empty($func['class'])) {
- $type = $actrec ? 'HHVM_FN' : 'HHVM_FUNCTION';
- $ret .= " $type({$func['name']}";
- } else {
- if ($actrec) {
- $type = in_array('static', $func['modifiers'])
- ? 'HHVM_STATIC_MN' : 'HHVM_MN';
- } else {
- $type = in_array('static', $func['modifiers'])
- ? 'HHVM_STATIC_METHOD' : 'HHVM_METHOD';
- }
- $ret .= " $type({$func['class']}, {$func['name']}";
- }
- // arguments
- $args = $actrec ? " auto this_ = ar_->m_this;\n" : '';
- foreach(array_values($func['args']) as $index => $arg) {
- if ($arg['reference']) {
- $type = $typemap['reference'];
- } else {
- $type = idx($typemap, $arg['type'], $typemap['object']);
- }
- if ($actrec) {
- $args .= " auto {$arg['name']} = getArg<{$type[2]}>(ar_, $index);\n";
- } else {
- $args .= ", {$type[1]} {$arg['name']}";
- }
- }
- if (!$actrec) {
- $ret .= "$args) {\n";
- } else if (!$alias) {
- $ret .= ')(ActRec* ar_) {'."\n$args\n";
- } else {
- $type = in_array('static', $alias['modifiers'])
- ? 'HHVM_STATIC_MN' : 'HHVM_MN';
- $ret .= <<<CPP
- )(ActRec* ar_) {
- return $type({$alias['class']}, {$alias['name']})(ar_);
- }
- CPP;
- return $ret;
- }
- // body
- if (!$alias) {
- $qualified_name = empty($func['class']) ? '' : "{$func['class']}::";
- $qualified_name .= $func['name'];
- $ret .= <<<CPP
- throw_not_implemented("$qualified_name");
- }
- CPP;
- return $ret;
- }
- // alias
- $ret .= " ";
- if (!empty($func['return']['type']) &&
- ($func['return']['type'] != 'void')) {
- $ret .= 'return ';
- }
- $comma = false;
- if (in_array('static', $alias['modifiers'])) {
- $ret .= "HHVM_STATIC_MN({$alias['class']}, {$alias['name']})".
- "(Unit::lookupClass(s_{$alias['class']}.get())";
- $comma = true;
- } else {
- $ret .= "HHVM_MN({$alias['class']}, {$alias['name']})(";
- }
- foreach($func['args'] as $arg) {
- if ($comma) $ret .= ', ';
- $comma = true;
- $ret .= $arg['name'];
- }
- return "$ret);\n}\n\n";
- }
- function outputExtensionCPP(string $dest, string $extname,
- array $funcs, array $classes): void {
- $fp = fopen($dest, 'w');
- fwrite($fp, "#include \"hphp/runtime/ext/extension.h\"\n");
- fwrite($fp, "namespace HPHP {\n");
- foreach($classes as $class) {
- fwrite($fp, "const StaticString s_{$class['name']}".
- "(\"{$class['name']}\");\n");
- if (empty($class['functions'])) continue;
- fwrite($fp, str_repeat('/', 78) . "\n// class {$class['name']}\n\n");
- foreach($class['functions'] as $func) {
- fwrite($fp, generateCPPStub($func, $classes));
- }
- }
- if (!empty($funcs)) {
- fwrite($fp, str_repeat('/', 78) . "\n// functions\n\n");
- foreach($funcs as $func) {
- fwrite($fp, generateCPPStub($func, $classes));
- }
- }
- fwrite($fp, str_repeat('/', 78) . "\n\n");
- fwrite($fp, "static class {$extname}Extension final : public Extension {\n");
- fwrite($fp, " public:\n");
- fwrite($fp, " {$extname}Extension() : Extension(\"{$extname}\") {}\n");
- fwrite($fp, " void moduleInit() override {\n");
- foreach($classes as $class) {
- if (empty($class['functions'])) continue;
- foreach($class['functions'] as $func) {
- $type = in_array('static', $func['modifiers'])
- ? 'HHVM_STATIC_ME' : 'HHVM_ME';
- fwrite($fp, " $type({$func['class']}, {$func['name']});\n");
- }
- }
- foreach($funcs as $func) {
- if (empty($func['name'])) continue;
- fwrite($fp, " HHVM_FE({$func['name']});\n");
- }
- fwrite($fp, " loadSystemlib();\n");
- fwrite($fp, " }\n");
- fwrite($fp, "} s_{$extname}_extension;\n\n");
- fwrite($fp, "// Uncomment for non-bundled module\n");
- fwrite($fp, "//HHVM_GET_MODULE(${extname});\n\n");
- fwrite($fp, str_repeat('/', 78) . "\n");
- fwrite($fp, "} // namespace HPHP\n");
- }
- if (empty($_SERVER['argv'][2])) {
- fwrite(STDERR, "Usage: {$_SERVER['argv'][0]} <phpdoc-root> <extname> ".
- "[ exname.cpp [ extname.php ] ]\n");
- exit;
- }
- $extname = $_SERVER['argv'][2];
- $ext = new HHVMDocExtension($extname, $_SERVER['argv'][1]);
- $cppfile = empty($_SERVER['argv'][3])
- ? "$extname.cpp" : $_SERVER['argv'][3];
- $phpfile = empty($_SERVER['argv'][4])
- ? "$extname.php" : $_SERVER['argv'][4];
- if (!preg_match('@^[a-zA-Z0-9_\.-]+$@', $extname)) {
- die("Invalid extension name: $extname\n");
- }
- $ext->setVerbose(true);
- outputSystemlib($phpfile,
- $ext->getFunctions(),
- $ext->getClasses());
- outputExtensionCPP($cppfile, $extname,
- $ext->getFunctions(),
- $ext->getClasses());