gdump.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
  2. * GObject introspection: Dump introspection data
  3. *
  4. * Copyright (C) 2008 Colin Walters <walters@verbum.org>
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, write to the
  18. * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  19. * Boston, MA 02111-1307, USA.
  20. */
  21. #include <stdlib.h>
  22. #include <glib.h>
  23. #include <glib-object.h>
  24. #include <gio/gio.h>
  25. /* This file is both compiled into libgirepository.so, and installed
  26. * on the filesystem. But for the dumper, we want to avoid linking
  27. * to libgirepository; see
  28. * https://bugzilla.gnome.org/show_bug.cgi?id=630342
  29. */
  30. #ifdef G_IREPOSITORY_COMPILATION
  31. #include "config.h"
  32. #include "girepository.h"
  33. #endif
  34. #include <string.h>
  35. static void
  36. escaped_printf (GOutputStream *out, const char *fmt, ...) G_GNUC_PRINTF (2, 3);
  37. static void
  38. escaped_printf (GOutputStream *out, const char *fmt, ...)
  39. {
  40. char *str;
  41. va_list args;
  42. gsize written;
  43. GError *error = NULL;
  44. va_start (args, fmt);
  45. str = g_markup_vprintf_escaped (fmt, args);
  46. if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error))
  47. {
  48. g_critical ("failed to write to iochannel: %s", error->message);
  49. g_clear_error (&error);
  50. }
  51. g_free (str);
  52. va_end (args);
  53. }
  54. static void
  55. goutput_write (GOutputStream *out, const char *str)
  56. {
  57. gsize written;
  58. GError *error = NULL;
  59. if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error))
  60. {
  61. g_critical ("failed to write to iochannel: %s", error->message);
  62. g_clear_error (&error);
  63. }
  64. }
  65. typedef GType (*GetTypeFunc)(void);
  66. typedef GQuark (*ErrorQuarkFunc)(void);
  67. static GType
  68. invoke_get_type (GModule *self, const char *symbol, GError **error)
  69. {
  70. GetTypeFunc sym;
  71. GType ret;
  72. if (!g_module_symbol (self, symbol, (void**)&sym))
  73. {
  74. g_set_error (error,
  75. G_IO_ERROR,
  76. G_IO_ERROR_FAILED,
  77. "Failed to find symbol '%s'", symbol);
  78. return G_TYPE_INVALID;
  79. }
  80. ret = sym ();
  81. if (ret == G_TYPE_INVALID)
  82. {
  83. g_set_error (error,
  84. G_IO_ERROR,
  85. G_IO_ERROR_FAILED,
  86. "Function '%s' returned G_TYPE_INVALID", symbol);
  87. }
  88. return ret;
  89. }
  90. static GQuark
  91. invoke_error_quark (GModule *self, const char *symbol, GError **error)
  92. {
  93. ErrorQuarkFunc sym;
  94. if (!g_module_symbol (self, symbol, (void**)&sym))
  95. {
  96. g_set_error (error,
  97. G_IO_ERROR,
  98. G_IO_ERROR_FAILED,
  99. "Failed to find symbol '%s'", symbol);
  100. return G_TYPE_INVALID;
  101. }
  102. return sym ();
  103. }
  104. static void
  105. dump_properties (GType type, GOutputStream *out)
  106. {
  107. guint i;
  108. guint n_properties;
  109. GParamSpec **props;
  110. if (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT)
  111. {
  112. GObjectClass *klass;
  113. klass = g_type_class_ref (type);
  114. props = g_object_class_list_properties (klass, &n_properties);
  115. }
  116. else
  117. {
  118. void *klass;
  119. klass = g_type_default_interface_ref (type);
  120. props = g_object_interface_list_properties (klass, &n_properties);
  121. }
  122. for (i = 0; i < n_properties; i++)
  123. {
  124. GParamSpec *prop;
  125. prop = props[i];
  126. if (prop->owner_type != type)
  127. continue;
  128. escaped_printf (out, " <property name=\"%s\" type=\"%s\" flags=\"%d\"/>\n",
  129. prop->name, g_type_name (prop->value_type), prop->flags);
  130. }
  131. g_free (props);
  132. }
  133. static void
  134. dump_signals (GType type, GOutputStream *out)
  135. {
  136. guint i;
  137. guint n_sigs;
  138. guint *sig_ids;
  139. sig_ids = g_signal_list_ids (type, &n_sigs);
  140. for (i = 0; i < n_sigs; i++)
  141. {
  142. guint sigid;
  143. GSignalQuery query;
  144. guint j;
  145. sigid = sig_ids[i];
  146. g_signal_query (sigid, &query);
  147. escaped_printf (out, " <signal name=\"%s\" return=\"%s\"",
  148. query.signal_name, g_type_name (query.return_type));
  149. if (query.signal_flags & G_SIGNAL_RUN_FIRST)
  150. escaped_printf (out, " when=\"first\"");
  151. else if (query.signal_flags & G_SIGNAL_RUN_LAST)
  152. escaped_printf (out, " when=\"last\"");
  153. else if (query.signal_flags & G_SIGNAL_RUN_CLEANUP)
  154. escaped_printf (out, " when=\"cleanup\"");
  155. #if GLIB_CHECK_VERSION(2, 29, 15)
  156. else if (query.signal_flags & G_SIGNAL_MUST_COLLECT)
  157. escaped_printf (out, " when=\"must-collect\"");
  158. #endif
  159. if (query.signal_flags & G_SIGNAL_NO_RECURSE)
  160. escaped_printf (out, " no-recurse=\"1\"");
  161. if (query.signal_flags & G_SIGNAL_DETAILED)
  162. escaped_printf (out, " detailed=\"1\"");
  163. if (query.signal_flags & G_SIGNAL_ACTION)
  164. escaped_printf (out, " action=\"1\"");
  165. if (query.signal_flags & G_SIGNAL_NO_HOOKS)
  166. escaped_printf (out, " no-hooks=\"1\"");
  167. goutput_write (out, ">\n");
  168. for (j = 0; j < query.n_params; j++)
  169. {
  170. escaped_printf (out, " <param type=\"%s\"/>\n",
  171. g_type_name (query.param_types[j]));
  172. }
  173. goutput_write (out, " </signal>\n");
  174. }
  175. g_free (sig_ids);
  176. }
  177. static void
  178. dump_object_type (GType type, const char *symbol, GOutputStream *out)
  179. {
  180. guint n_interfaces;
  181. guint i;
  182. GType *interfaces;
  183. escaped_printf (out, " <class name=\"%s\" get-type=\"%s\"",
  184. g_type_name (type), symbol);
  185. if (type != G_TYPE_OBJECT)
  186. {
  187. GString *parent_str;
  188. GType parent;
  189. gboolean first = TRUE;
  190. parent = g_type_parent (type);
  191. parent_str = g_string_new ("");
  192. while (parent != G_TYPE_INVALID)
  193. {
  194. if (first)
  195. first = FALSE;
  196. else
  197. g_string_append_c (parent_str, ',');
  198. g_string_append (parent_str, g_type_name (parent));
  199. parent = g_type_parent (parent);
  200. }
  201. escaped_printf (out, " parents=\"%s\"", parent_str->str);
  202. g_string_free (parent_str, TRUE);
  203. }
  204. if (G_TYPE_IS_ABSTRACT (type))
  205. escaped_printf (out, " abstract=\"1\"");
  206. goutput_write (out, ">\n");
  207. interfaces = g_type_interfaces (type, &n_interfaces);
  208. for (i = 0; i < n_interfaces; i++)
  209. {
  210. GType itype = interfaces[i];
  211. escaped_printf (out, " <implements name=\"%s\"/>\n",
  212. g_type_name (itype));
  213. }
  214. g_free (interfaces);
  215. dump_properties (type, out);
  216. dump_signals (type, out);
  217. goutput_write (out, " </class>\n");
  218. }
  219. static void
  220. dump_interface_type (GType type, const char *symbol, GOutputStream *out)
  221. {
  222. guint n_interfaces;
  223. guint i;
  224. GType *interfaces;
  225. escaped_printf (out, " <interface name=\"%s\" get-type=\"%s\">\n",
  226. g_type_name (type), symbol);
  227. interfaces = g_type_interface_prerequisites (type, &n_interfaces);
  228. for (i = 0; i < n_interfaces; i++)
  229. {
  230. GType itype = interfaces[i];
  231. if (itype == G_TYPE_OBJECT)
  232. {
  233. /* Treat this as implicit for now; in theory GInterfaces are
  234. * supported on things like GstMiniObject, but right now
  235. * the introspection system only supports GObject.
  236. * http://bugzilla.gnome.org/show_bug.cgi?id=559706
  237. */
  238. continue;
  239. }
  240. escaped_printf (out, " <prerequisite name=\"%s\"/>\n",
  241. g_type_name (itype));
  242. }
  243. g_free (interfaces);
  244. dump_properties (type, out);
  245. dump_signals (type, out);
  246. goutput_write (out, " </interface>\n");
  247. }
  248. static void
  249. dump_boxed_type (GType type, const char *symbol, GOutputStream *out)
  250. {
  251. escaped_printf (out, " <boxed name=\"%s\" get-type=\"%s\"/>\n",
  252. g_type_name (type), symbol);
  253. }
  254. static void
  255. dump_flags_type (GType type, const char *symbol, GOutputStream *out)
  256. {
  257. guint i;
  258. GFlagsClass *klass;
  259. klass = g_type_class_ref (type);
  260. escaped_printf (out, " <flags name=\"%s\" get-type=\"%s\">\n",
  261. g_type_name (type), symbol);
  262. for (i = 0; i < klass->n_values; i++)
  263. {
  264. GFlagsValue *value = &(klass->values[i]);
  265. escaped_printf (out, " <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
  266. value->value_name, value->value_nick, value->value);
  267. }
  268. goutput_write (out, " </flags>\n");
  269. }
  270. static void
  271. dump_enum_type (GType type, const char *symbol, GOutputStream *out)
  272. {
  273. guint i;
  274. GEnumClass *klass;
  275. klass = g_type_class_ref (type);
  276. escaped_printf (out, " <enum name=\"%s\" get-type=\"%s\">\n",
  277. g_type_name (type), symbol);
  278. for (i = 0; i < klass->n_values; i++)
  279. {
  280. GEnumValue *value = &(klass->values[i]);
  281. escaped_printf (out, " <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
  282. value->value_name, value->value_nick, value->value);
  283. }
  284. goutput_write (out, " </enum>");
  285. }
  286. static void
  287. dump_fundamental_type (GType type, const char *symbol, GOutputStream *out)
  288. {
  289. guint n_interfaces;
  290. guint i;
  291. GType *interfaces;
  292. GString *parent_str;
  293. GType parent;
  294. gboolean first = TRUE;
  295. escaped_printf (out, " <fundamental name=\"%s\" get-type=\"%s\"",
  296. g_type_name (type), symbol);
  297. if (G_TYPE_IS_ABSTRACT (type))
  298. escaped_printf (out, " abstract=\"1\"");
  299. if (G_TYPE_IS_INSTANTIATABLE (type))
  300. escaped_printf (out, " instantiatable=\"1\"");
  301. parent = g_type_parent (type);
  302. parent_str = g_string_new ("");
  303. while (parent != G_TYPE_INVALID)
  304. {
  305. if (first)
  306. first = FALSE;
  307. else
  308. g_string_append_c (parent_str, ',');
  309. if (!g_type_name (parent))
  310. break;
  311. g_string_append (parent_str, g_type_name (parent));
  312. parent = g_type_parent (parent);
  313. }
  314. if (parent_str->len > 0)
  315. escaped_printf (out, " parents=\"%s\"", parent_str->str);
  316. g_string_free (parent_str, TRUE);
  317. goutput_write (out, ">\n");
  318. interfaces = g_type_interfaces (type, &n_interfaces);
  319. for (i = 0; i < n_interfaces; i++)
  320. {
  321. GType itype = interfaces[i];
  322. escaped_printf (out, " <implements name=\"%s\"/>\n",
  323. g_type_name (itype));
  324. }
  325. g_free (interfaces);
  326. goutput_write (out, " </fundamental>\n");
  327. }
  328. static void
  329. dump_type (GType type, const char *symbol, GOutputStream *out)
  330. {
  331. switch (g_type_fundamental (type))
  332. {
  333. case G_TYPE_OBJECT:
  334. dump_object_type (type, symbol, out);
  335. break;
  336. case G_TYPE_INTERFACE:
  337. dump_interface_type (type, symbol, out);
  338. break;
  339. case G_TYPE_BOXED:
  340. dump_boxed_type (type, symbol, out);
  341. break;
  342. case G_TYPE_FLAGS:
  343. dump_flags_type (type, symbol, out);
  344. break;
  345. case G_TYPE_ENUM:
  346. dump_enum_type (type, symbol, out);
  347. break;
  348. case G_TYPE_POINTER:
  349. /* GValue, etc. Just skip them. */
  350. break;
  351. default:
  352. dump_fundamental_type (type, symbol, out);
  353. break;
  354. }
  355. }
  356. static void
  357. dump_error_quark (GQuark quark, const char *symbol, GOutputStream *out)
  358. {
  359. escaped_printf (out, " <error-quark function=\"%s\" domain=\"%s\"/>\n",
  360. symbol, g_quark_to_string (quark));
  361. }
  362. /**
  363. * g_irepository_dump:
  364. * @arg: Comma-separated pair of input and output filenames
  365. * @error: a %GError
  366. *
  367. * Argument specified is a comma-separated pair of filenames; i.e. of
  368. * the form "input.txt,output.xml". The input file should be a
  369. * UTF-8 Unix-line-ending text file, with each line containing either
  370. * "get-type:" followed by the name of a GType _get_type function, or
  371. * "error-quark:" followed by the name of an error quark function. No
  372. * extra whitespace is allowed.
  373. *
  374. * The output file should already exist, but be empty. This function will
  375. * overwrite its contents.
  376. *
  377. * Returns: %TRUE on success, %FALSE on error
  378. */
  379. #ifndef G_IREPOSITORY_COMPILATION
  380. static gboolean
  381. dump_irepository (const char *arg, GError **error) G_GNUC_UNUSED;
  382. static gboolean
  383. dump_irepository (const char *arg, GError **error)
  384. #else
  385. gboolean
  386. g_irepository_dump (const char *arg, GError **error)
  387. #endif
  388. {
  389. GHashTable *output_types;
  390. char **args;
  391. GFile *input_file;
  392. GFile *output_file;
  393. GFileInputStream *input;
  394. GFileOutputStream *output;
  395. GDataInputStream *in;
  396. GModule *self;
  397. gboolean caught_error = FALSE;
  398. self = g_module_open (NULL, 0);
  399. if (!self)
  400. {
  401. g_set_error (error,
  402. G_IO_ERROR,
  403. G_IO_ERROR_FAILED,
  404. "failed to open self: %s",
  405. g_module_error ());
  406. return FALSE;
  407. }
  408. args = g_strsplit (arg, ",", 2);
  409. input_file = g_file_new_for_path (args[0]);
  410. output_file = g_file_new_for_path (args[1]);
  411. g_strfreev (args);
  412. input = g_file_read (input_file, NULL, error);
  413. if (input == NULL)
  414. return FALSE;
  415. output = g_file_replace (output_file, NULL, FALSE, 0, NULL, error);
  416. if (output == NULL)
  417. {
  418. g_input_stream_close (G_INPUT_STREAM (input), NULL, NULL);
  419. return FALSE;
  420. }
  421. goutput_write (G_OUTPUT_STREAM (output), "<?xml version=\"1.0\"?>\n");
  422. goutput_write (G_OUTPUT_STREAM (output), "<dump>\n");
  423. output_types = g_hash_table_new (NULL, NULL);
  424. in = g_data_input_stream_new (G_INPUT_STREAM (input));
  425. g_object_unref (input);
  426. while (TRUE)
  427. {
  428. gsize len;
  429. char *line = g_data_input_stream_read_line (in, &len, NULL, NULL);
  430. const char *function;
  431. if (line == NULL || *line == '\0')
  432. {
  433. g_free (line);
  434. break;
  435. }
  436. g_strchomp (line);
  437. if (strncmp (line, "get-type:", strlen ("get-type:")) == 0)
  438. {
  439. GType type;
  440. function = line + strlen ("get-type:");
  441. type = invoke_get_type (self, function, error);
  442. if (type == G_TYPE_INVALID)
  443. {
  444. g_printerr ("Invalid GType function: '%s'\n", function);
  445. caught_error = TRUE;
  446. g_free (line);
  447. break;
  448. }
  449. if (g_hash_table_lookup (output_types, (gpointer) type))
  450. goto next;
  451. g_hash_table_insert (output_types, (gpointer) type, (gpointer) type);
  452. dump_type (type, function, G_OUTPUT_STREAM (output));
  453. }
  454. else if (strncmp (line, "error-quark:", strlen ("error-quark:")) == 0)
  455. {
  456. GQuark quark;
  457. function = line + strlen ("error-quark:");
  458. quark = invoke_error_quark (self, function, error);
  459. if (quark == 0)
  460. {
  461. g_printerr ("Invalid error quark function: '%s'\n", function);
  462. caught_error = TRUE;
  463. g_free (line);
  464. break;
  465. }
  466. dump_error_quark (quark, function, G_OUTPUT_STREAM (output));
  467. }
  468. next:
  469. g_free (line);
  470. }
  471. g_hash_table_destroy (output_types);
  472. goutput_write (G_OUTPUT_STREAM (output), "</dump>\n");
  473. {
  474. GError **ioerror;
  475. /* Avoid overwriting an earlier set error */
  476. if (caught_error)
  477. ioerror = NULL;
  478. else
  479. ioerror = error;
  480. if (!g_input_stream_close (G_INPUT_STREAM (in), NULL, ioerror))
  481. return FALSE;
  482. if (!g_output_stream_close (G_OUTPUT_STREAM (output), NULL, ioerror))
  483. return FALSE;
  484. }
  485. return !caught_error;
  486. }