3 +----------------------------------------------------------------------+
5 +----------------------------------------------------------------------+
6 | Copyright (c) 2014 Facebook, Inc. (http://www.facebook.com) |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 A helper script to check that HNI signatures in a PHP file match how the
19 functions are defined in the C++ file.
21 Currently only matches functions, so won't match classes/methods.
23 // expects argv[1] to be the C++ file, argv[2] to be the PHP file
24 if (empty($_SERVER['argv'][2])) {
25 fwrite(STDERR
, "Usage: {$_SERVER['argv'][0]} <extfile.cpp> <extfile.php>\n");
29 function parse_php_functions(string $file):
30 ConstMap
<string, Pair
<string, ConstVector
<string>>> {
31 $source = file_get_contents($file);
36 // Don't handle methods yet, so function can't be indented
38 "#<<[^>]*__Native([^>]*)>>\nfunction +([^(]*)\(([^)]*)\) *: *(.+?);#m";
42 if (preg_match_all($function_regex, $source, $matches, PREG_SET_ORDER
)) {
43 foreach($matches as $match) {
44 $nativeArgs = $match[1];
46 if (strpos($nativeArgs, '"ActRec"') !== false) {
47 // ActRec functions have a specific structure
49 $argTypes = Vector
{'actrec'};
52 $retType = explode('<', $match[4], 2)[0];
53 $argTypes = Vector
{};
55 $args = preg_split('/\s*,\s*/', $argList);
56 if (count($args) > 7 && (in_array('float', $args)
57 ||
in_array('double', $args))) {
59 $argTypes = Vector
{'actrec'};
60 } else if (count($args) > 15) {
62 $argTypes = Vector
{'actrec'};
64 foreach($args as $arg) {
65 $type = preg_split('/\s*\$/', $arg)[0];
66 $type = explode('<', $type, 2)[0];
68 // Special case varargs
69 $vargTypes = Vector
{'int'};
70 $vargTypes->addAll($argTypes);
71 $vargTypes[] = 'array';
72 $argTypes = $vargTypes;
80 $functions[strtolower($name)] = Pair
{ $retType, $argTypes };
86 function parse_cpp_functions(string $file):
87 ConstMap
<string, Pair
<string, ConstVector
<string>>> {
88 $source = file_get_contents($file);
93 // Don't handle methods yet, so function can't be indented
95 "#^(?:static )?(\S+) +HHVM_FUNCTION\(([^,)]+)(?:, *)?([^)]*)\)#m";
100 if (preg_match_all($function_regex, $source, $matches, PREG_SET_ORDER
)) {
101 foreach($matches as $match) {
103 $argList = $match[3];
104 $retType = $match[1];
105 $argTypes = Vector
{};
107 $args = preg_split('/\s*,\s*/', $argList);
108 foreach($args as $arg) {
109 $type = preg_split('# */ *#', $arg)[0];
110 $type = implode(' ', explode(' ', $type, -1));
114 $functions[strtolower($name)] = Pair
{ $retType, $argTypes };
120 function parse_php_methods(string $file):
121 ConstMap
<string, Pair
<string, ConstVector
<string>>> {
122 $source = file_get_contents($file);
127 $class_regex = "#class ([^\\s{/]+)[^{/\\)]*\\{(.*?)\n\\}#ms";
129 "#<<[^>]*__Native([^>]*)>>\n\\s*.*?function +([^(]*)\(([^)]*)\) *: *(.+?);#m";
134 if (preg_match_all($class_regex, $source, $classes, PREG_SET_ORDER
)) {
135 foreach ($classes as $class) {
139 if (preg_match_all($method_regex, $source, $matches, PREG_SET_ORDER
)) {
140 foreach($matches as $match) {
141 $nativeArgs = $match[1];
143 if (strpos($nativeArgs, '"ActRec"') !== false) {
144 // ActRec functions have a specific structure
146 $argTypes = Vector
{'actrec'};
148 $argList = $match[3];
149 $retType = explode('<', $match[4], 2)[0];
150 $argTypes = Vector
{};
152 $args = preg_split('/\s*,\s*/', $argList);
153 if (count($args) > 7 && (in_array('float', $args)
154 ||
in_array('double', $args))) {
156 $argTypes = Vector
{'actrec'};
157 } else if (count($args) > 15) {
159 $argTypes = Vector
{'actrec'};
161 foreach($args as $arg) {
162 $type = preg_split('/\s*\$/', $arg)[0];
163 $type = explode('<', $type, 2)[0];
164 if ($type == '...') {
165 // Special case varargs
166 $vargTypes = Vector
{'int'};
167 $vargTypes->addAll($argTypes);
168 $vargTypes[] = 'array';
169 $argTypes = $vargTypes;
177 $methods[strtolower("$cname::$mname")] = Pair
{ $retType, $argTypes };
186 function parse_cpp_methods(string $file):
187 ConstMap
<string, Pair
<string, ConstVector
<string>>> {
188 $source = file_get_contents($file);
193 // Don't handle methods yet, so function can't be indented
195 "#^(?:static )?(\S+) +HHVM_(?:STATIC_)?METHOD\(([^,)]+),\s+([^,)]+)(?:, *)?([^)]*)\)#m";
200 if (preg_match_all($method_regex, $source, $matches, PREG_SET_ORDER
)) {
201 foreach($matches as $match) {
204 $argList = $match[4];
205 $retType = $match[1];
206 $argTypes = Vector
{};
208 $args = preg_split('/\s*,\s*/', $argList);
209 foreach($args as $arg) {
210 $type = preg_split('# */ *#', $arg)[0];
211 $type = implode(' ', explode(' ', $type, -1));
215 $methods[strtolower("$cname::$mname")] = Pair
{ $retType, $argTypes };
221 function match_return_type(string $php, string $cpp): bool {
222 if ($php[0] == '?') {
223 $expected = 'Variant';
225 switch (strtolower($php)) {
232 $expected = 'int64_t';
236 $expected = 'double';
242 $expected = 'String';
248 $expected = 'Resource';
252 $expected = 'Variant';
255 $expected = 'TypedValue*';
259 $expected = 'Object';
263 // Special case for ints
267 return $cpp == $expected;
270 function match_arg_type(string $php, string $cpp): bool {
271 if ($php[0] == '@') {
272 $php = substr($php, 1);
274 if ($php[0] == '?') {
275 $expected = 'const Variant&';
277 switch (strtolower(strtok($php, ' &'))) {
284 $expected = 'int64_t';
288 $expected = 'double';
291 // Shouldn't have void as an argument type
294 $expected = 'const String&';
297 $expected = 'const Array&';
300 $expected = 'const Resource&';
304 $expected = 'const Variant&';
307 $expected = 'ActRec*';
311 $expected = 'const Object&';
316 // Special case for ints
320 return $cpp == $expected;
323 function check_types(ConstMap
<string, Pair
<string, ConstVector
<string>>> $php,
324 ConstMap
<string, Pair
<string, ConstVector
<string>>> $cpp):
327 foreach($php as $name => $types) {
328 if (!isset($cpp[$name])) {
330 printf("Unimplemented native function '%s'\n", $name);
333 $cppTypes = $cpp[$name];
334 if (!match_return_type($types[0], $cppTypes[0])) {
336 printf("Mismatched return type for function '%s'. PHP: %s C++: %s\n",
337 $name, $types[0], $cppTypes[0]);
339 if ($types[1]->count() != $cppTypes[1]->count()) {
341 printf("Unequal number of arguments for function '%s'\n", $name);
344 foreach($types[1] as $idx => $t) {
345 if (!match_arg_type($t, $cppTypes[1][$idx])) {
347 printf("Mismatched argument type for function '%s' at index '%d'."
348 . " PHP: %s C++: %s\n", $name, $idx, $t, $cppTypes[1][$idx]);
355 $phpFuncs = parse_php_functions($_SERVER['argv'][2]);
356 $cppFuncs = parse_cpp_functions($_SERVER['argv'][1]);
358 $cppMeths = parse_cpp_methods($_SERVER['argv'][1]);
359 $phpMeths = parse_php_methods($_SERVER['argv'][2]);
361 $funcs = check_types($phpFuncs, $cppFuncs);
362 $meths = check_types($phpMeths, $cppMeths);
364 if ($funcs ||
$meths) {
365 echo "See https://github.com/facebook/hhvm/wiki/Extension-API for what types",