winapi_extract 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. #!/usr/bin/perl
  2. # Copyright 2002 Patrik Stridvall
  3. #
  4. # This library is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU Lesser General Public
  6. # License as published by the Free Software Foundation; either
  7. # version 2.1 of the License, or (at your option) any later version.
  8. #
  9. # This library is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. # Lesser General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU Lesser General Public
  15. # License along with this library; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  17. #
  18. use strict;
  19. use warnings 'all';
  20. BEGIN {
  21. $0 =~ m%^(.*?/?tools)/winapi/winapi_extract$%;
  22. require "$1/winapi/setup.pm";
  23. }
  24. use config qw(
  25. files_skip files_filter get_spec_files
  26. $current_dir $wine_dir $winapi_dir
  27. );
  28. use output qw($output);
  29. use winapi_extract_options qw($options);
  30. if($options->progress) {
  31. $output->enable_progress;
  32. } else {
  33. $output->disable_progress;
  34. }
  35. use c_parser;
  36. use function;
  37. use type;
  38. use winapi_function;
  39. use vars qw($win16api $win32api @winapis);
  40. if ($options->implemented || $options->stub_statistics || $options->winetest) {
  41. require winapi;
  42. import winapi qw($win16api $win32api @winapis);
  43. }
  44. my %module2entries;
  45. my %module2spec_file;
  46. if($options->winetest) {
  47. local $_;
  48. foreach my $spec_file (get_spec_files("winelib")) {
  49. my $entries = [];
  50. my $module = $spec_file;
  51. $module =~ s/^.*?([^\/]*)\.spec$/$1/;
  52. my $type = "win32";
  53. open(IN, "< $wine_dir/$spec_file") || die "Error: Can't open $wine_dir/$spec_file: $!\n";
  54. my $header = 1;
  55. my $lookahead = 0;
  56. while($lookahead || defined($_ = <IN>)) {
  57. $lookahead = 0;
  58. s/^\s*?(.*?)\s*$/$1/; # remove whitespace at beginning and end of line
  59. s/^(.*?)\s*#.*$/$1/; # remove comments
  60. /^$/ && next; # skip empty lines
  61. if($header) {
  62. if(/^(?:\d+|@)/) {
  63. $header = 0;
  64. $lookahead = 1;
  65. }
  66. next;
  67. }
  68. if(/^(\d+|@)\s+stdcall\s+(\w+)\s*\(\s*([^\)]*)\s*\)/) {
  69. my $ordinal = $1;
  70. my $name = $2;
  71. my @args = split(/\s+/, $3);
  72. push @$entries, [$name, "undef", \@args];
  73. }
  74. }
  75. close(IN);
  76. $module2spec_file{$module} = $spec_file;
  77. $module2entries{$module} = $entries;
  78. }
  79. }
  80. my %specifications;
  81. sub documentation_specifications($) {
  82. my $function = shift;
  83. my @debug_channels = @{$function->debug_channels};
  84. my $documentation = $function->documentation;
  85. my $documentation_line = $function->documentation_line;
  86. my $return_type = $function->return_type;
  87. my $linkage = $function->linkage;
  88. my $internal_name = $function->internal_name;
  89. if($linkage eq "static") {
  90. return;
  91. }
  92. local $_;
  93. foreach (split(/\n/, $documentation)) {
  94. if(/^\s*\*\s*(\S+)\s*[\(\[]\s*(\w+)\s*\.\s*(\S+)\s*[\)\]]/) {
  95. my $external_name = $1;
  96. my $module = lc($2);
  97. my $ordinal = $3;
  98. if($ordinal eq "@") {
  99. if(1 || !exists($specifications{$module}{unfixed}{$external_name})) {
  100. $specifications{$module}{unfixed}{$external_name}{ordinal} = $ordinal;
  101. $specifications{$module}{unfixed}{$external_name}{external_name} = $external_name;
  102. $specifications{$module}{unfixed}{$external_name}{function} = $function;
  103. } else {
  104. $output->write("$external_name ($module.$ordinal) already exists\n");
  105. }
  106. } elsif($ordinal =~ /^\d+$/) {
  107. if(1 || !exists($specifications{$module}{fixed}{$ordinal})) {
  108. $specifications{$module}{fixed}{$ordinal}{ordinal} = $ordinal;
  109. $specifications{$module}{fixed}{$ordinal}{external_name} = $external_name;
  110. $specifications{$module}{fixed}{$ordinal}{function} = $function;
  111. } else {
  112. $output->write("$external_name ($module.$ordinal) already exists\n");
  113. }
  114. } elsif($ordinal eq "init") {
  115. if(!exists($specifications{$module}{init})) {
  116. $specifications{$module}{init}{function} = $function;
  117. } else {
  118. $output->write("$external_name ($module.$ordinal) already exists\n");
  119. }
  120. } else {
  121. if(!exists($specifications{$module}{unknown}{$external_name})) {
  122. $specifications{$module}{unknown}{$external_name}{ordinal} = $ordinal;
  123. $specifications{$module}{unknown}{$external_name}{external_name} = $external_name;
  124. $specifications{$module}{unknown}{$external_name}{function} = $function;
  125. } else {
  126. $output->write("$external_name ($module.$ordinal) already exists\n");
  127. }
  128. }
  129. if($options->debug) {
  130. $output->write("$external_name ($module.$ordinal)\n");
  131. }
  132. }
  133. }
  134. }
  135. my %module_pseudo_stub;
  136. sub statements_pseudo_stub($) {
  137. my $function = shift;
  138. my $pseudo_stub = 0;
  139. my $statements = $function->statements;
  140. if(defined($statements) && $statements =~ /FIXME[^;]*stub/s) {
  141. if($options->win16) {
  142. my $external_name16 = $function->external_name16;
  143. foreach my $module16 ($function->modules16) {
  144. $module_pseudo_stub{$module16}{$external_name16}++;
  145. $pseudo_stub = 1;
  146. }
  147. }
  148. if($options->win32) {
  149. my $external_name32 = $function->external_name32;
  150. foreach my $module32 ($function->modules32) {
  151. $module_pseudo_stub{$module32}{$external_name32}++;
  152. $pseudo_stub = 1;
  153. }
  154. }
  155. }
  156. return $pseudo_stub;
  157. }
  158. my @h_files = ();
  159. if($options->headers) {
  160. @h_files = $options->h_files;
  161. @h_files = files_skip(@h_files);
  162. @h_files = files_filter("winelib", @h_files);
  163. }
  164. my @c_files = ();
  165. if($options->pseudo_implemented || $options->pseudo_stub_statistics) {
  166. @c_files = $options->c_files;
  167. @c_files = files_skip(@c_files);
  168. @c_files = files_filter("winelib", @c_files);
  169. }
  170. my $progress_output;
  171. my $progress_current = 0;
  172. my $progress_max = scalar(@h_files) + scalar(@c_files);
  173. foreach my $file (@h_files, @c_files) {
  174. my %functions;
  175. $progress_current++;
  176. {
  177. open(IN, "< $file") || die "Error: Can't open $file: $!\n";
  178. local $/ = undef;
  179. $_ = <IN>;
  180. close(IN);
  181. }
  182. my $max_line = 0;
  183. {
  184. local $_ = $_;
  185. while(s/^.*?\n//) { $max_line++; }
  186. if($_) { $max_line++; }
  187. }
  188. my $parser = new c_parser($file);
  189. my $function;
  190. my $line;
  191. my $update_output = sub {
  192. my $progress = "";
  193. my $prefix = "";
  194. $progress .= "$file (file $progress_current of $progress_max)";
  195. $prefix .= "$file:";
  196. if(defined($function)) {
  197. my $name = $function->name;
  198. my $begin_line = $function->begin_line;
  199. my $begin_column = $function->begin_column;
  200. $progress .= ": function $name";
  201. $prefix .= "$begin_line.$begin_column: function $name: ";
  202. } else {
  203. $prefix .= " ";
  204. }
  205. if(defined($line)) {
  206. $progress .= ": line $line of $max_line";
  207. }
  208. $output->progress($progress);
  209. $output->prefix($prefix);
  210. };
  211. &$update_output();
  212. my $found_function = sub {
  213. $function = shift;
  214. my $name = $function->name;
  215. $functions{$name} = $function;
  216. if ($function->statements) {
  217. &$update_output();
  218. }
  219. my $old_function;
  220. if($options->implemented || $options->stub_statistics) {
  221. $old_function = 'winapi_function'->new;
  222. } else {
  223. $old_function = 'function'->new;
  224. }
  225. $old_function->file($function->file);
  226. $old_function->debug_channels([]); # FIXME: Not complete
  227. $old_function->documentation_line(0); # FIXME: Not complete
  228. $old_function->documentation(""); # FIXME: Not complete
  229. $old_function->function_line($function->begin_line());
  230. $old_function->linkage($function->linkage);
  231. $old_function->return_type($function->return_type);
  232. $old_function->calling_convention($function->calling_convention);
  233. $old_function->internal_name($function->name);
  234. if (defined($function->argument_types)) {
  235. $old_function->argument_types([@{$function->argument_types}]);
  236. }
  237. if (defined($function->argument_names)) {
  238. $old_function->argument_names([@{$function->argument_names}]);
  239. }
  240. $old_function->argument_documentations([]); # FIXME: Not complete
  241. $old_function->statements_line($function->statements_line);
  242. $old_function->statements($function->statements);
  243. if($options->winetest) {
  244. documentation_specifications($old_function);
  245. }
  246. if ($function->statements) {
  247. $function = undef;
  248. &$update_output();
  249. } else {
  250. $function = undef;
  251. }
  252. my $pseudo_stub = 0;
  253. if ($options->pseudo_implemented || $options->pseudo_stub_statistics) {
  254. $pseudo_stub = statements_pseudo_stub($old_function);
  255. }
  256. my $module = $old_function->module;
  257. my $external_name = $old_function->external_name;
  258. my $statements = $old_function->statements;
  259. if ($options->pseudo_implemented && $module && $external_name && $statements) {
  260. my @external_names = split(/\s*&\s*/, $external_name);
  261. my @modules = split(/\s*&\s*/, $module);
  262. my @external_names2;
  263. while(defined(my $external_name = shift @external_names) &&
  264. defined(my $module = shift @modules))
  265. {
  266. if ($pseudo_stub) {
  267. $output->write("$module.$external_name: pseudo implemented\n");
  268. } else {
  269. $output->write("$module.$external_name: implemented\n");
  270. }
  271. }
  272. }
  273. };
  274. $parser->set_found_function_callback($found_function);
  275. my $found_line = sub {
  276. $line = shift;
  277. &$update_output;
  278. };
  279. $parser->set_found_line_callback($found_line);
  280. my $found_type = sub {
  281. my $type = shift;
  282. &$update_output();
  283. my $kind = $type->kind;
  284. my $_name = $type->_name;
  285. my $name = $type->name;
  286. foreach my $field ($type->fields) {
  287. my $field_type_name = $field->type_name;
  288. my $field_name = $field->name;
  289. if ($options->struct && $kind =~ /^(?:struct|union)$/) {
  290. if ($name) {
  291. $output->write("$name:$field_type_name:$field_name\n");
  292. } else {
  293. $output->write("$kind $_name:$field_type_name:$field_name\n");
  294. }
  295. }
  296. }
  297. return 1;
  298. };
  299. $parser->set_found_type_callback($found_type);
  300. {
  301. my $line = 1;
  302. my $column = 0;
  303. if(!$parser->parse_c_file(\$_, \$line, \$column)) {
  304. $output->write("can't parse file\n");
  305. }
  306. }
  307. $output->prefix("");
  308. }
  309. if($options->implemented && !$options->pseudo_implemented) {
  310. foreach my $winapi (@winapis) {
  311. my $type = $winapi->name;
  312. if($type eq "win16" && !$options->win16) { next; }
  313. if($type eq "win32" && !$options->win32) { next; }
  314. foreach my $module ($winapi->all_modules) {
  315. foreach my $external_name ($winapi->all_functions_in_module($module)) {
  316. my $external_calling_convention =
  317. $winapi->function_external_calling_convention_in_module($module, $external_name);
  318. if($external_calling_convention eq "forward") {
  319. (my $forward_module, my $forward_external_name) =
  320. $winapi->function_forward_final_destination($module, $external_name);
  321. my $forward_external_calling_convention =
  322. $winapi->function_external_calling_convention_in_module($forward_module, $forward_external_name);
  323. if(!defined($forward_external_calling_convention)) {
  324. next;
  325. }
  326. $external_calling_convention = $forward_external_calling_convention;
  327. }
  328. if ($external_calling_convention ne "stub") {
  329. $output->write("*.spec: $module.$external_name: implemented\n");
  330. }
  331. }
  332. }
  333. }
  334. }
  335. sub output_function($$$$$) {
  336. local *OUT = shift;
  337. my $type = shift;
  338. my $ordinal = shift;
  339. my $external_name = shift;
  340. my $function = shift;
  341. my $internal_name = $function->internal_name;
  342. my $return_kind;
  343. my $calling_convention;
  344. my $refargument_kinds;
  345. if($type eq "win16") {
  346. $return_kind = $function->return_kind16 || "undef";
  347. $calling_convention = $function->calling_convention16 || "undef";
  348. $refargument_kinds = $function->argument_kinds16;
  349. } elsif($type eq "win32") {
  350. $return_kind = $function->return_kind32 || "undef";
  351. $calling_convention = $function->calling_convention32 || "undef";
  352. $refargument_kinds = $function->argument_kinds32;
  353. }
  354. if(defined($refargument_kinds)) {
  355. my @argument_kinds = map { $_ || "undef"; } @$refargument_kinds;
  356. print OUT "$ordinal $calling_convention $external_name(@argument_kinds) $internal_name\n";
  357. } else {
  358. print OUT "$ordinal $calling_convention $external_name() $internal_name # FIXME: arguments undefined\n";
  359. }
  360. }
  361. if($options->stub_statistics) {
  362. foreach my $winapi (@winapis) {
  363. my $type = $winapi->name;
  364. if($type eq "win16" && !$options->win16) { next; }
  365. if($type eq "win32" && !$options->win32) { next; }
  366. my %module_counts;
  367. foreach my $module ($winapi->all_modules) {
  368. foreach my $external_name ($winapi->all_functions_in_module($module)) {
  369. my $external_calling_convention =
  370. $winapi->function_external_calling_convention_in_module($module, $external_name);
  371. if($external_calling_convention !~ /^(?:forward|stub)$/) {
  372. if($module_pseudo_stub{$module}{$external_name}) {
  373. $external_calling_convention = "pseudo_stub";
  374. }
  375. } elsif($external_calling_convention eq "forward") {
  376. (my $forward_module, my $forward_external_name) =
  377. $winapi->function_forward_final_destination($module, $external_name);
  378. my $forward_external_calling_convention =
  379. $winapi->function_external_calling_convention_in_module($forward_module, $forward_external_name);
  380. if(!defined($forward_external_calling_convention)) {
  381. next;
  382. }
  383. if($forward_external_calling_convention ne "stub" &&
  384. $module_pseudo_stub{$forward_module}{$forward_external_name})
  385. {
  386. $forward_external_calling_convention = "pseudo_stub";
  387. }
  388. $external_calling_convention = "forward_$forward_external_calling_convention";
  389. }
  390. $module_counts{$module}{$external_calling_convention}++;
  391. }
  392. }
  393. foreach my $module ($winapi->all_modules) {
  394. my $pseudo_stubs = $module_counts{$module}{pseudo_stub} || 0;
  395. my $real_stubs = $module_counts{$module}{stub} || 0;
  396. my $forward_pseudo_stubs = $module_counts{$module}{forward_pseudo_stub} || 0;
  397. my $forward_real_stubs = $module_counts{$module}{forward_stub} || 0;
  398. my $forwards = 0;
  399. my $total = 0;
  400. foreach my $calling_convention (keys(%{$module_counts{$module}})) {
  401. my $count = $module_counts{$module}{$calling_convention};
  402. if($calling_convention =~ /^forward/) {
  403. $forwards += $count;
  404. }
  405. $total += $count;
  406. }
  407. if($total > 0) {
  408. my $stubs = $real_stubs + $pseudo_stubs;
  409. $output->write("*.c: $module: ");
  410. $output->write("$stubs of $total functions are stubs ($real_stubs real, $pseudo_stubs pseudo) " .
  411. "and $forwards are forwards\n");
  412. }
  413. if($forwards > 0) {
  414. my $forward_stubs = $forward_real_stubs + $forward_pseudo_stubs;
  415. $output->write("*.c: $module: ");
  416. $output->write("$forward_stubs of $forwards forwarded functions are stubs " .
  417. "($forward_real_stubs real, $forward_pseudo_stubs pseudo)\n");
  418. }
  419. }
  420. }
  421. }
  422. if($options->winetest) {
  423. foreach my $module ($win32api->all_modules) {
  424. my $type = "win32";
  425. my $package = $module;
  426. $package =~ s/\.dll$//;
  427. $package =~ s/\./_/g;
  428. my @entries;
  429. foreach my $external_name (sort(keys(%{$specifications{$module}{unknown}}))) {
  430. my $entry = $specifications{$module}{unknown}{$external_name};
  431. push @entries, $entry;
  432. }
  433. foreach my $ordinal (sort {$a <=> $b} keys(%{$specifications{$module}{fixed}})) {
  434. my $entry = $specifications{$module}{fixed}{$ordinal};
  435. push @entries, $entry;
  436. }
  437. foreach my $external_name (sort(keys(%{$specifications{$module}{unfixed}}))) {
  438. my $entry = $specifications{$module}{unfixed}{$external_name};
  439. push @entries, $entry;
  440. }
  441. my $n = 0;
  442. foreach my $entry (@entries) {
  443. my $external_name = $entry->{external_name};
  444. my $ordinal = $entry->{ordinal};
  445. my $function = $entry->{function};
  446. my $return_kind = $function->return_kind32 || "undef";
  447. my $calling_convention = $function->calling_convention32 || "undef";
  448. my $refargument_kinds = $function->argument_kinds32;
  449. my @argument_kinds;
  450. if(defined($refargument_kinds)) {
  451. @argument_kinds = map { $_ || "undef"; } @$refargument_kinds;
  452. }
  453. next if $calling_convention ne "stdcall";
  454. next if $external_name eq "\@";
  455. if($n == 0) {
  456. open(OUT, "> $wine_dir/programs/winetest/include/${package}.pm") || die "Error: Can't open $wine_dir/programs/winetest/include/${package}.pm: $!\n";
  457. print OUT "package ${package};\n";
  458. print OUT "\n";
  459. print OUT "use strict;\n";
  460. print OUT "\n";
  461. print OUT "require Exporter;\n";
  462. print OUT "\n";
  463. print OUT "use wine;\n";
  464. print OUT "use vars qw(\@ISA \@EXPORT \@EXPORT_OK);\n";
  465. print OUT "\n";
  466. print OUT "\@ISA = qw(Exporter);\n";
  467. print OUT "\@EXPORT = qw();\n";
  468. print OUT "\@EXPORT_OK = qw();\n";
  469. print OUT "\n";
  470. print OUT "my \$module_declarations = {\n";
  471. } elsif($n > 0) {
  472. print OUT ",\n";
  473. }
  474. print OUT " \"\Q$external_name\E\" => [\"$return_kind\", [";
  475. my $m = 0;
  476. foreach my $argument_kind (@argument_kinds) {
  477. if($m > 0) {
  478. print OUT ", ";
  479. }
  480. print OUT "\"$argument_kind\"";
  481. $m++;
  482. }
  483. print OUT "]]";
  484. $n++;
  485. }
  486. if($n > 0) {
  487. print OUT "\n";
  488. print OUT "};\n";
  489. print OUT "\n";
  490. print OUT "&wine::declare(\"$module\",\%\$module_declarations);\n";
  491. print OUT "push \@EXPORT, map { \"&\" . \$_; } sort(keys(\%\$module_declarations));\n";
  492. print OUT "1;\n";
  493. close(OUT);
  494. }
  495. }
  496. }