Remove Variant's append and appendRef member functions
[hiphop-php.git] / hphp / tools / check_native_signatures.php
bloba6dfaff0a5f62f815c5cb8ef6cbacf400350a648
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) > 5) {
57 $retType = 'actrec';
58 $argTypes = Vector {'actrec'};
59 } else {
60 foreach($args as $arg) {
61 $type = preg_split('/\s*\$/', $arg)[0];
62 $type = explode('<', $type, 2)[0];
63 if ($type == '...') {
64 // Special case varargs
65 $vargTypes = Vector {'int'};
66 $vargTypes->addAll($argTypes);
67 $vargTypes[] = 'array';
68 $argTypes = $vargTypes;
69 } else {
70 $argTypes[] = $type;
76 $functions[$name] = Pair { $retType, $argTypes };
79 return $functions;
82 function parse_cpp_functions(string $file):
83 ConstMap<string, Pair<string, ConstVector<string>>> {
84 $source = file_get_contents($file);
85 if (!$source) {
86 return ImmMap {};
89 // Don't handle methods yet, so function can't be indented
90 static $function_regex =
91 "#^(?:static )?(\S+) +HHVM_FUNCTION\(([^,)]+)(?:, *)?([^)]*)\)#m";
93 $functions = Map {};
95 if (preg_match_all($function_regex, $source, $matches, PREG_SET_ORDER)) {
96 foreach($matches as $match) {
97 $name = $match[2];
98 $argList = $match[3];
99 $retType = $match[1];
100 $argTypes = Vector {};
101 if ($argList) {
102 $args = preg_split('/\s*,\s*/', $argList);
103 foreach($args as $arg) {
104 $type = preg_split('# */ *#', $arg)[0];
105 $type = implode(' ', explode(' ', $type, -1));
106 $argTypes[] = $type;
109 $functions[$name] = Pair { $retType, $argTypes };
112 return $functions;
115 function match_return_type(string $php, string $cpp): bool {
116 if ($php[0] == '?') {
117 $expected = 'Variant';
118 } else {
119 switch (strtolower($php)) {
120 case 'bool':
121 case 'boolean':
122 $expected = 'bool';
123 break;
124 case 'int':
125 case 'long':
126 $expected = 'int64_t';
127 break;
128 case 'float':
129 case 'double':
130 $expected = 'double';
131 break;
132 case 'void':
133 $expected = 'void';
134 break;
135 case 'string':
136 $expected = 'String';
137 break;
138 case 'array':
139 $expected = 'Array';
140 break;
141 case 'resource':
142 $expected = 'Resource';
143 break;
144 case 'mixed':
145 case 'callable':
146 $expected = 'Variant';
147 break;
148 case 'actrec':
149 $expected = 'TypedValue*';
150 break;
151 case 'object':
152 default:
153 $expected = 'Object';
154 break;
157 // Special case for ints
158 if ($cpp == 'int') {
159 $cpp = 'int64_t';
161 return $cpp == $expected;
164 function match_arg_type(string $php, string $cpp): bool {
165 if ($php[0] == '@') {
166 $php = substr($php, 1);
168 if ($php[0] == '?') {
169 $expected = 'const Variant&';
170 } else {
171 switch (strtolower($php)) {
172 case 'bool':
173 case 'boolean':
174 $expected = 'bool';
175 break;
176 case 'int':
177 case 'long':
178 $expected = 'int64_t';
179 break;
180 case 'float':
181 case 'double':
182 $expected = 'double';
183 break;
184 case 'void':
185 // Shouldn't have void as an argument type
186 return false;
187 case 'string':
188 $expected = 'const String&';
189 break;
190 case 'array':
191 $expected = 'const Array&';
192 break;
193 case 'resource':
194 $expected = 'const Resource&';
195 break;
196 case 'mixed':
197 case 'callable':
198 $expected = 'const Variant&';
199 break;
200 case 'actrec':
201 $expected = 'ActRec*';
202 break;
203 case 'object':
204 default:
205 $expected = 'const Object&';
206 break;
209 // References must be a variant type
210 if ($php[strlen($php)-1] == '&') {
211 if ($expected != 'const Variant&') {
212 return false;
213 } else {
214 $expected = 'VRefParam';
217 $cpp = trim($cpp);
218 // Special case for ints
219 if ($cpp == 'int') {
220 $cpp = 'int64_t';
222 return $cpp == $expected;
225 function check_types(ConstMap<string, Pair<string, ConstVector<string>>> $php,
226 ConstMap<string, Pair<string, ConstVector<string>>> $cpp):
227 bool {
228 $errored = false;
229 foreach($php as $name => $types) {
230 if (!isset($cpp[$name])) {
231 $errored = true;
232 printf("Unimplemented native function '%s'\n", $name);
233 continue;
235 $cppTypes = $cpp[$name];
236 if (!match_return_type($types[0], $cppTypes[0])) {
237 $errored = true;
238 printf("Mismatched return type for function '%s'. PHP: %s C++: %s\n",
239 $name, $types[0], $cppTypes[0]);
241 if ($types[1]->count() != $cppTypes[1]->count()) {
242 $errored = true;
243 printf("Unequal number of arguments for function '%s'\n", $name);
244 continue;
246 foreach($types[1] as $idx => $t) {
247 if (!match_arg_type($t, $cppTypes[1][$idx])) {
248 $errored = true;
249 printf("Mismatched argument type for function '%s' at index '%d'."
250 . " PHP: %s C++: %s\n", $name, $idx, $t, $cppTypes[1][$idx]);
254 return $errored;
257 $phpFuncs = parse_php_functions($_SERVER['argv'][2]);
258 $cppFuncs = parse_cpp_functions($_SERVER['argv'][1]);
260 if (check_types($phpFuncs, $cppFuncs)) {
261 echo "See https://github.com/facebook/hhvm/wiki/Extension-API for what types",
262 " map to what\n";