Remove VariantStrongBind and CVarStrongBind
[hiphop-php.git] / hphp / tools / check_native_signatures.php
blob8771b04089fe9d80e8982820eeea351507bce7d6
1 <?hh
2 /*
3 +----------------------------------------------------------------------+
4 | HipHop for PHP |
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");
26 exit(1);
29 function parse_php_functions(string $file):
30 ConstMap<string, Pair<string, ConstVector<string>>> {
31 $source = file_get_contents($file);
32 if (!$source) {
33 return ImmMap {};
36 // Don't handle methods yet, so function can't be indented
37 static $function_regex =
38 "#<<[^>]*__Native([^>]*)>>\nfunction +([^(]*)\(([^)]*)\) *: *(.+?);#m";
40 $functions = Map {};
42 if (preg_match_all($function_regex, $source, $matches, PREG_SET_ORDER)) {
43 foreach($matches as $match) {
44 $nativeArgs = $match[1];
45 $name = $match[2];
46 if (strpos($nativeArgs, '"ActRec"') !== false) {
47 // ActRec functions have a specific structure
48 $retType = 'actrec';
49 $argTypes = Vector {'actrec'};
50 } else {
51 $argList = $match[3];
52 $retType = explode('<', $match[4], 2)[0];
53 $argTypes = Vector {};
54 if ($argList) {
55 $args = preg_split('/\s*,\s*/', $argList);
56 if (count($args) > 7 && (in_array('float', $args)
57 || in_array('double', $args))) {
58 $retType = 'actrec';
59 $argTypes = Vector {'actrec'};
60 } else if (count($args) > 15) {
61 $retType = 'actrec';
62 $argTypes = Vector {'actrec'};
63 } else {
64 foreach($args as $arg) {
65 $type = preg_split('/\s*\$/', $arg)[0];
66 $type = explode('<', $type, 2)[0];
67 if ($type == '...') {
68 // Special case varargs
69 $vargTypes = Vector {'int'};
70 $vargTypes->addAll($argTypes);
71 $vargTypes[] = 'array';
72 $argTypes = $vargTypes;
73 } else {
74 $argTypes[] = $type;
80 $functions[strtolower($name)] = Pair { $retType, $argTypes };
83 return $functions;
86 function parse_cpp_functions(string $file):
87 ConstMap<string, Pair<string, ConstVector<string>>> {
88 $source = file_get_contents($file);
89 if (!$source) {
90 return ImmMap {};
93 // Don't handle methods yet, so function can't be indented
94 static $function_regex =
95 "#^(?:static )?(\S+) +HHVM_FUNCTION\(([^,)]+)(?:, *)?([^)]*)\)#m";
97 $functions = Map {};
99 if (preg_match_all($function_regex, $source, $matches, PREG_SET_ORDER)) {
100 foreach($matches as $match) {
101 $name = $match[2];
102 $argList = $match[3];
103 $retType = $match[1];
104 $argTypes = Vector {};
105 if ($argList) {
106 $args = preg_split('/\s*,\s*/', $argList);
107 foreach($args as $arg) {
108 $type = preg_split('# */ *#', $arg)[0];
109 $type = implode(' ', explode(' ', $type, -1));
110 $argTypes[] = $type;
113 $functions[strtolower($name)] = Pair { $retType, $argTypes };
116 return $functions;
119 function parse_php_methods(string $file):
120 ConstMap<string, Pair<string, ConstVector<string>>> {
121 $source = file_get_contents($file);
122 if (!$source) {
123 return ImmMap {};
126 static $class_regex = "#class ([^\\s{/]+)[^{/\\)]*\\{(.*?)\n\\}#ms";
127 static $method_regex =
128 "#<<[^>]*__Native([^>]*)>>\n\\s*.*?function +([^(]*)\(([^)]*)\) *: *(.+?);#m";
130 $methods = Map {};
132 if (preg_match_all($class_regex, $source, $classes, PREG_SET_ORDER)) {
133 foreach ($classes as $class) {
134 $cname = $class[1];
135 $source = $class[2];
136 if (preg_match_all($method_regex, $source, $matches, PREG_SET_ORDER)) {
137 foreach($matches as $match) {
138 $nativeArgs = $match[1];
139 $mname = $match[2];
140 if (strpos($nativeArgs, '"ActRec"') !== false) {
141 // ActRec functions have a specific structure
142 $retType = 'actrec';
143 $argTypes = Vector {'actrec'};
144 } else {
145 $argList = $match[3];
146 $retType = explode('<', $match[4], 2)[0];
147 $argTypes = Vector {};
148 if ($argList) {
149 $args = preg_split('/\s*,\s*/', $argList);
150 if (count($args) > 7 && (in_array('float', $args)
151 || in_array('double', $args))) {
152 $retType = 'actrec';
153 $argTypes = Vector {'actrec'};
154 } else if (count($args) > 15) {
155 $retType = 'actrec';
156 $argTypes = Vector {'actrec'};
157 } else {
158 foreach($args as $arg) {
159 $type = preg_split('/\s*\$/', $arg)[0];
160 $type = explode('<', $type, 2)[0];
161 if ($type == '...') {
162 // Special case varargs
163 $vargTypes = Vector {'int'};
164 $vargTypes->addAll($argTypes);
165 $vargTypes[] = 'array';
166 $argTypes = $vargTypes;
167 } else {
168 $argTypes[] = $type;
174 $methods[strtolower("$cname::$mname")] = Pair { $retType, $argTypes };
180 return $methods;
183 function parse_cpp_methods(string $file):
184 ConstMap<string, Pair<string, ConstVector<string>>> {
185 $source = file_get_contents($file);
186 if (!$source) {
187 return ImmMap {};
190 // Don't handle methods yet, so function can't be indented
191 static $method_regex =
192 "#^(?:static )?(\S+) +HHVM_(?:STATIC_)?METHOD\(([^,)]+),\s+([^,)]+)(?:, *)?([^)]*)\)#m";
194 $methods = Map {};
196 if (preg_match_all($method_regex, $source, $matches, PREG_SET_ORDER)) {
197 foreach($matches as $match) {
198 $cname = $match[2];
199 $mname = $match[3];
200 $argList = $match[4];
201 $retType = $match[1];
202 $argTypes = Vector {};
203 if ($argList) {
204 $args = preg_split('/\s*,\s*/', $argList);
205 foreach($args as $arg) {
206 $type = preg_split('# */ *#', $arg)[0];
207 $type = implode(' ', explode(' ', $type, -1));
208 $argTypes[] = $type;
211 $methods[strtolower("$cname::$mname")] = Pair { $retType, $argTypes };
214 return $methods;
217 function match_return_type(string $php, string $cpp): bool {
218 if ($php[0] == '?') {
219 $expected = 'Variant';
220 } else {
221 switch (strtolower($php)) {
222 case 'bool':
223 case 'boolean':
224 $expected = 'bool';
225 break;
226 case 'int':
227 case 'long':
228 $expected = 'int64_t';
229 break;
230 case 'float':
231 case 'double':
232 $expected = 'double';
233 break;
234 case 'void':
235 $expected = 'void';
236 break;
237 case 'string':
238 $expected = 'String';
239 break;
240 case 'array':
241 $expected = 'Array';
242 break;
243 case 'resource':
244 $expected = 'Resource';
245 break;
246 case 'mixed':
247 case 'callable':
248 $expected = 'Variant';
249 break;
250 case 'actrec':
251 $expected = 'TypedValue*';
252 break;
253 case 'object':
254 default:
255 $expected = 'Object';
256 break;
259 // Special case for ints
260 if ($cpp == 'int') {
261 $cpp = 'int64_t';
263 return $cpp == $expected;
266 function match_arg_type(string $php, string $cpp): bool {
267 if ($php[0] == '@') {
268 $php = substr($php, 1);
270 if ($php[0] == '?') {
271 $expected = 'const Variant&';
272 } else {
273 switch (strtolower(strtok($php, ' &'))) {
274 case 'bool':
275 case 'boolean':
276 $expected = 'bool';
277 break;
278 case 'int':
279 case 'long':
280 $expected = 'int64_t';
281 break;
282 case 'float':
283 case 'double':
284 $expected = 'double';
285 break;
286 case 'void':
287 // Shouldn't have void as an argument type
288 return false;
289 case 'string':
290 $expected = 'const String&';
291 break;
292 case 'array':
293 $expected = 'const Array&';
294 break;
295 case 'resource':
296 $expected = 'const Resource&';
297 break;
298 case 'mixed':
299 case 'callable':
300 $expected = 'const Variant&';
301 break;
302 case 'actrec':
303 $expected = 'ActRec*';
304 break;
305 case 'object':
306 default:
307 $expected = 'const Object&';
308 break;
311 // References must be a variant type
312 if ($php[strlen($php)-1] == '&') {
313 if ($expected != 'const Variant&') {
314 return false;
315 } else {
316 $expected = 'VRefParam';
319 $cpp = trim($cpp);
320 // Special case for ints
321 if ($cpp == 'int') {
322 $cpp = 'int64_t';
324 return $cpp == $expected;
327 function check_types(ConstMap<string, Pair<string, ConstVector<string>>> $php,
328 ConstMap<string, Pair<string, ConstVector<string>>> $cpp):
329 bool {
330 $errored = false;
331 foreach($php as $name => $types) {
332 if (!isset($cpp[$name])) {
333 $errored = true;
334 printf("Unimplemented native function '%s'\n", $name);
335 continue;
337 $cppTypes = $cpp[$name];
338 if (!match_return_type($types[0], $cppTypes[0])) {
339 $errored = true;
340 printf("Mismatched return type for function '%s'. PHP: %s C++: %s\n",
341 $name, $types[0], $cppTypes[0]);
343 if ($types[1]->count() != $cppTypes[1]->count()) {
344 $errored = true;
345 printf("Unequal number of arguments for function '%s'\n", $name);
346 continue;
348 foreach($types[1] as $idx => $t) {
349 if (!match_arg_type($t, $cppTypes[1][$idx])) {
350 $errored = true;
351 printf("Mismatched argument type for function '%s' at index '%d'."
352 . " PHP: %s C++: %s\n", $name, $idx, $t, $cppTypes[1][$idx]);
356 return $errored;
359 $phpFuncs = parse_php_functions($_SERVER['argv'][2]);
360 $cppFuncs = parse_cpp_functions($_SERVER['argv'][1]);
362 $cppMeths = parse_cpp_methods($_SERVER['argv'][1]);
363 $phpMeths = parse_php_methods($_SERVER['argv'][2]);
365 $funcs = check_types($phpFuncs, $cppFuncs);
366 $meths = check_types($phpMeths, $cppMeths);
368 if ($funcs || $meths) {
369 echo "See https://github.com/facebook/hhvm/wiki/Extension-API for what types",
370 " map to what\n";