PageRenderTime 54ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/hphp/tools/check_native_signatures.php

http://github.com/facebook/hiphop-php
PHP | 367 lines | 312 code | 23 blank | 32 comment | 62 complexity | 3b43f3aae2a5f1dcfa951d72c7f14905 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
  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. +----------------------------------------------------------------------+
  16. */
  17. /*
  18. A helper script to check that HNI signatures in a PHP file match how the
  19. functions are defined in the C++ file.
  20. Currently only matches functions, so won't match classes/methods.
  21. */
  22. // expects argv[1] to be the C++ file, argv[2] to be the PHP file
  23. if (empty($_SERVER['argv'][2])) {
  24. fwrite(STDERR, "Usage: {$_SERVER['argv'][0]} <extfile.cpp> <extfile.php>\n");
  25. exit(1);
  26. }
  27. function parse_php_functions(string $file):
  28. ConstMap<string, Pair<string, ConstVector<string>>> {
  29. $source = file_get_contents($file);
  30. if (!$source) {
  31. return ImmMap {};
  32. }
  33. // Don't handle methods yet, so function can't be indented
  34. $function_regex =
  35. "#<<[^>]*__Native([^>]*)>>\nfunction +([^(]*)\(([^)]*)\) *: *(.+?);#m";
  36. $functions = Map {};
  37. $matches = [];
  38. if (preg_match_all($function_regex, $source, $matches, PREG_SET_ORDER)) {
  39. foreach($matches as $match) {
  40. $nativeArgs = $match[1];
  41. $name = $match[2];
  42. if (strpos($nativeArgs, '"ActRec"') !== false) {
  43. // ActRec functions have a specific structure
  44. $retType = 'actrec';
  45. $argTypes = Vector {'actrec'};
  46. } else {
  47. $argList = $match[3];
  48. $retType = explode('<', $match[4], 2)[0];
  49. $argTypes = Vector {};
  50. if ($argList) {
  51. $args = preg_split('/\s*,\s*/', $argList);
  52. if (count($args) > 7 && (in_array('float', $args)
  53. || in_array('double', $args))) {
  54. $retType = 'actrec';
  55. $argTypes = Vector {'actrec'};
  56. } else if (count($args) > 15) {
  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;
  71. }
  72. }
  73. }
  74. }
  75. }
  76. $functions[strtolower($name)] = Pair { $retType, $argTypes };
  77. }
  78. }
  79. return $functions;
  80. }
  81. function parse_cpp_functions(string $file):
  82. ConstMap<string, Pair<string, ConstVector<string>>> {
  83. $source = file_get_contents($file);
  84. if (!$source) {
  85. return ImmMap {};
  86. }
  87. // Don't handle methods yet, so function can't be indented
  88. $function_regex =
  89. "#^(?:static )?(\S+) +HHVM_FUNCTION\(([^,)]+)(?:, *)?([^)]*)\)#m";
  90. $functions = Map {};
  91. $matches = [];
  92. if (preg_match_all($function_regex, $source, $matches, PREG_SET_ORDER)) {
  93. foreach($matches as $match) {
  94. $name = $match[2];
  95. $argList = $match[3];
  96. $retType = $match[1];
  97. $argTypes = Vector {};
  98. if ($argList) {
  99. $args = preg_split('/\s*,\s*/', $argList);
  100. foreach($args as $arg) {
  101. $type = preg_split('# */ *#', $arg)[0];
  102. $type = implode(' ', explode(' ', $type, -1));
  103. $argTypes[] = $type;
  104. }
  105. }
  106. $functions[strtolower($name)] = Pair { $retType, $argTypes };
  107. }
  108. }
  109. return $functions;
  110. }
  111. function parse_php_methods(string $file):
  112. ConstMap<string, Pair<string, ConstVector<string>>> {
  113. $source = file_get_contents($file);
  114. if (!$source) {
  115. return ImmMap {};
  116. }
  117. $class_regex = "#class ([^\\s{/]+)[^{/\\)]*\\{(.*?)\n\\}#ms";
  118. $method_regex =
  119. "#<<[^>]*__Native([^>]*)>>\n\\s*.*?function +([^(]*)\(([^)]*)\) *: *(.+?);#m";
  120. $methods = Map {};
  121. $classes = [];
  122. if (preg_match_all($class_regex, $source, $classes, PREG_SET_ORDER)) {
  123. foreach ($classes as $class) {
  124. $cname = $class[1];
  125. $source = $class[2];
  126. $matches = [];
  127. if (preg_match_all($method_regex, $source, $matches, PREG_SET_ORDER)) {
  128. foreach($matches as $match) {
  129. $nativeArgs = $match[1];
  130. $mname = $match[2];
  131. if (strpos($nativeArgs, '"ActRec"') !== false) {
  132. // ActRec functions have a specific structure
  133. $retType = 'actrec';
  134. $argTypes = Vector {'actrec'};
  135. } else {
  136. $argList = $match[3];
  137. $retType = explode('<', $match[4], 2)[0];
  138. $argTypes = Vector {};
  139. if ($argList) {
  140. $args = preg_split('/\s*,\s*/', $argList);
  141. if (count($args) > 7 && (in_array('float', $args)
  142. || in_array('double', $args))) {
  143. $retType = 'actrec';
  144. $argTypes = Vector {'actrec'};
  145. } else if (count($args) > 15) {
  146. $retType = 'actrec';
  147. $argTypes = Vector {'actrec'};
  148. } else {
  149. foreach($args as $arg) {
  150. $type = preg_split('/\s*\$/', $arg)[0];
  151. $type = explode('<', $type, 2)[0];
  152. if ($type == '...') {
  153. // Special case varargs
  154. $vargTypes = Vector {'int'};
  155. $vargTypes->addAll($argTypes);
  156. $vargTypes[] = 'array';
  157. $argTypes = $vargTypes;
  158. } else {
  159. $argTypes[] = $type;
  160. }
  161. }
  162. }
  163. }
  164. }
  165. $methods[strtolower("$cname::$mname")] = Pair { $retType, $argTypes };
  166. }
  167. }
  168. }
  169. }
  170. return $methods;
  171. }
  172. function parse_cpp_methods(string $file):
  173. ConstMap<string, Pair<string, ConstVector<string>>> {
  174. $source = file_get_contents($file);
  175. if (!$source) {
  176. return ImmMap {};
  177. }
  178. // Don't handle methods yet, so function can't be indented
  179. $method_regex =
  180. "#^(?:static )?(\S+) +HHVM_(?:STATIC_)?METHOD\(([^,)]+),\s+([^,)]+)(?:, *)?([^)]*)\)#m";
  181. $methods = Map {};
  182. $matches = [];
  183. if (preg_match_all($method_regex, $source, $matches, PREG_SET_ORDER)) {
  184. foreach($matches as $match) {
  185. $cname = $match[2];
  186. $mname = $match[3];
  187. $argList = $match[4];
  188. $retType = $match[1];
  189. $argTypes = Vector {};
  190. if ($argList) {
  191. $args = preg_split('/\s*,\s*/', $argList);
  192. foreach($args as $arg) {
  193. $type = preg_split('# */ *#', $arg)[0];
  194. $type = implode(' ', explode(' ', $type, -1));
  195. $argTypes[] = $type;
  196. }
  197. }
  198. $methods[strtolower("$cname::$mname")] = Pair { $retType, $argTypes };
  199. }
  200. }
  201. return $methods;
  202. }
  203. function match_return_type(string $php, string $cpp): bool {
  204. if ($php[0] == '?') {
  205. $expected = 'Variant';
  206. } else {
  207. switch (strtolower($php)) {
  208. case 'bool':
  209. case 'boolean':
  210. $expected = 'bool';
  211. break;
  212. case 'int':
  213. case 'long':
  214. $expected = 'int64_t';
  215. break;
  216. case 'float':
  217. case 'double':
  218. $expected = 'double';
  219. break;
  220. case 'void':
  221. $expected = 'void';
  222. break;
  223. case 'string':
  224. $expected = 'String';
  225. break;
  226. case 'array':
  227. $expected = 'Array';
  228. break;
  229. case 'resource':
  230. $expected = 'Resource';
  231. break;
  232. case 'mixed':
  233. case 'callable':
  234. $expected = 'Variant';
  235. break;
  236. case 'actrec':
  237. $expected = 'TypedValue*';
  238. break;
  239. case 'object':
  240. default:
  241. $expected = 'Object';
  242. break;
  243. }
  244. }
  245. // Special case for ints
  246. if ($cpp == 'int') {
  247. $cpp = 'int64_t';
  248. }
  249. return $cpp == $expected;
  250. }
  251. function match_arg_type(string $php, string $cpp): bool {
  252. if ($php[0] == '@') {
  253. $php = substr($php, 1);
  254. }
  255. if ($php[0] == '?') {
  256. $expected = 'const Variant&';
  257. } else {
  258. switch (strtolower(strtok($php, ' &'))) {
  259. case 'bool':
  260. case 'boolean':
  261. $expected = 'bool';
  262. break;
  263. case 'int':
  264. case 'long':
  265. $expected = 'int64_t';
  266. break;
  267. case 'float':
  268. case 'double':
  269. $expected = 'double';
  270. break;
  271. case 'void':
  272. // Shouldn't have void as an argument type
  273. return false;
  274. case 'string':
  275. $expected = 'const String&';
  276. break;
  277. case 'array':
  278. $expected = 'const Array&';
  279. break;
  280. case 'resource':
  281. $expected = 'const Resource&';
  282. break;
  283. case 'mixed':
  284. case 'callable':
  285. $expected = 'const Variant&';
  286. break;
  287. case 'actrec':
  288. $expected = 'ActRec*';
  289. break;
  290. case 'object':
  291. default:
  292. $expected = 'const Object&';
  293. break;
  294. }
  295. }
  296. $cpp = trim($cpp);
  297. // Special case for ints
  298. if ($cpp == 'int') {
  299. $cpp = 'int64_t';
  300. }
  301. return $cpp == $expected;
  302. }
  303. function check_types(ConstMap<string, Pair<string, ConstVector<string>>> $php,
  304. ConstMap<string, Pair<string, ConstVector<string>>> $cpp):
  305. bool {
  306. $errored = false;
  307. foreach($php as $name => $types) {
  308. if (!isset($cpp[$name])) {
  309. $errored = true;
  310. printf("Unimplemented native function '%s'\n", $name);
  311. continue;
  312. }
  313. $cppTypes = $cpp[$name];
  314. if (!match_return_type($types[0], $cppTypes[0])) {
  315. $errored = true;
  316. printf("Mismatched return type for function '%s'. PHP: %s C++: %s\n",
  317. $name, $types[0], $cppTypes[0]);
  318. }
  319. if ($types[1]->count() != $cppTypes[1]->count()) {
  320. $errored = true;
  321. printf("Unequal number of arguments for function '%s'\n", $name);
  322. continue;
  323. }
  324. foreach($types[1] as $idx => $t) {
  325. if (!match_arg_type($t, $cppTypes[1][$idx])) {
  326. $errored = true;
  327. printf("Mismatched argument type for function '%s' at index '%d'."
  328. . " PHP: %s C++: %s\n", $name, $idx, $t, $cppTypes[1][$idx]);
  329. }
  330. }
  331. }
  332. return $errored;
  333. }
  334. $phpFuncs = parse_php_functions($_SERVER['argv'][2]);
  335. $cppFuncs = parse_cpp_functions($_SERVER['argv'][1]);
  336. $cppMeths = parse_cpp_methods($_SERVER['argv'][1]);
  337. $phpMeths = parse_php_methods($_SERVER['argv'][2]);
  338. $funcs = check_types($phpFuncs, $cppFuncs);
  339. $meths = check_types($phpMeths, $cppMeths);
  340. if ($funcs || $meths) {
  341. echo "See https://github.com/facebook/hhvm/wiki/Extension-API for what types",
  342. " map to what\n";
  343. }