1 /*
2  * peas-plugin-loader-c.c
3  * This file is part of libpeas
4  *
5  * Copyright (C) 2008 - Jesse van den Kieboom
6  *
7  * libpeas is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * libpeas is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <string.h>
27 
28 #include "peas-plugin-loader-c.h"
29 
30 #include "peas-extension-base.h"
31 #include "peas-object-module.h"
32 #include "peas-plugin-info-priv.h"
33 
34 typedef struct {
35   GMutex lock;
36 
37   GHashTable *loaded_plugins;
38 } PeasPluginLoaderCPrivate;
39 
40 G_DEFINE_TYPE_WITH_PRIVATE (PeasPluginLoaderC,
41                             peas_plugin_loader_c,
42                             PEAS_TYPE_PLUGIN_LOADER)
43 
44 #define GET_PRIV(o) \
45   (peas_plugin_loader_c_get_instance_private (o))
46 
47 static GQuark quark_extension_type = 0;
48 static const gchar *intern_plugin_info = NULL;
49 
50 static gboolean
peas_plugin_loader_c_load(PeasPluginLoader * loader,PeasPluginInfo * info)51 peas_plugin_loader_c_load (PeasPluginLoader *loader,
52                            PeasPluginInfo   *info)
53 {
54   PeasPluginLoaderC *cloader = PEAS_PLUGIN_LOADER_C (loader);
55   PeasPluginLoaderCPrivate *priv = GET_PRIV (cloader);
56 
57   g_mutex_lock (&priv->lock);
58 
59   if (!g_hash_table_lookup_extended (priv->loaded_plugins,
60                                      info->filename,
61                                      NULL, (gpointer *) &info->loader_data))
62     {
63       const gchar *module_name, *module_dir;
64 
65       module_name = peas_plugin_info_get_module_name (info);
66       module_dir = peas_plugin_info_get_module_dir (info);
67 
68       if (info->embedded != NULL)
69         {
70           info->loader_data = peas_object_module_new_embedded (module_name,
71                                                                info->embedded);
72         }
73       else
74         {
75           /* Force all C modules to be resident in case they
76            * use libraries that do not deal well with reloading.
77            * Furthermore, we use local linkage to improve module isolation.
78            */
79           info->loader_data = peas_object_module_new_full (module_name,
80                                                            module_dir,
81                                                            TRUE, TRUE);
82         }
83 
84       if (!g_type_module_use (G_TYPE_MODULE (info->loader_data)))
85         g_clear_object (&info->loader_data);
86 
87       g_hash_table_insert (priv->loaded_plugins,
88                            g_strdup (info->filename), info->loader_data);
89     }
90 
91   g_mutex_unlock (&priv->lock);
92   return info->loader_data != NULL;
93 }
94 
95 static void
peas_plugin_loader_c_unload(PeasPluginLoader * loader,PeasPluginInfo * info)96 peas_plugin_loader_c_unload (PeasPluginLoader *loader,
97                              PeasPluginInfo   *info)
98 {
99   /* Don't bother unloading the plugin's
100    * GTypeModule as it is always resident
101    */
102   info->loader_data = NULL;
103 }
104 
105 static gboolean
peas_plugin_loader_c_provides_extension(PeasPluginLoader * loader,PeasPluginInfo * info,GType exten_type)106 peas_plugin_loader_c_provides_extension  (PeasPluginLoader *loader,
107                                           PeasPluginInfo   *info,
108                                           GType             exten_type)
109 {
110   return peas_object_module_provides_object (info->loader_data, exten_type);
111 }
112 
113 static PeasExtension *
peas_plugin_loader_c_create_extension(PeasPluginLoader * loader,PeasPluginInfo * info,GType exten_type,guint n_parameters,GParameter * parameters)114 peas_plugin_loader_c_create_extension (PeasPluginLoader *loader,
115                                        PeasPluginInfo   *info,
116                                        GType             exten_type,
117                                        guint             n_parameters,
118                                        GParameter       *parameters)
119 {
120   GParameter *exten_parameters;
121   gpointer instance;
122 
123   /* We want to add a "plugin-info" property so we can pass it to
124    * the extension if it inherits from PeasExtensionBase. No need to
125    * actually "duplicate" the GValues, a memcpy is sufficient as the
126    * source GValues are longer lived than our local copy.
127    */
128   exten_parameters = g_newa (GParameter, n_parameters + 1);
129   memcpy (exten_parameters, parameters, sizeof (GParameter) * n_parameters);
130 
131   /* Initialize our additional property.
132    * If the instance does not have a plugin-info property
133    * then PeasObjectModule will remove the property.
134    */
135   exten_parameters[n_parameters].name = intern_plugin_info;
136   memset (&exten_parameters[n_parameters].value, 0, sizeof (GValue));
137   g_value_init (&exten_parameters[n_parameters].value, PEAS_TYPE_PLUGIN_INFO);
138   g_value_set_boxed (&exten_parameters[n_parameters].value, info);
139 
140   instance = peas_object_module_create_object (info->loader_data,
141                                                exten_type,
142                                                n_parameters + 1,
143                                                exten_parameters);
144 
145   g_value_unset (&exten_parameters[n_parameters].value);
146 
147   if (instance == NULL)
148     return NULL;
149 
150   g_return_val_if_fail (G_IS_OBJECT (instance), NULL);
151   g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (instance, exten_type), NULL);
152 
153   /* We have to remember which interface we are instantiating
154    * for the deprecated peas_extension_get_extension_type().
155    */
156   g_object_set_qdata (instance, quark_extension_type,
157                       GSIZE_TO_POINTER (exten_type));
158 
159   return instance;
160 }
161 
162 static void
peas_plugin_loader_c_init(PeasPluginLoaderC * cloader)163 peas_plugin_loader_c_init (PeasPluginLoaderC *cloader)
164 {
165   PeasPluginLoaderCPrivate *priv = GET_PRIV (cloader);
166 
167   g_mutex_init (&priv->lock);
168 
169   /* loaded_plugins maps PeasPluginInfo:filename to a PeasObjectModule */
170   priv->loaded_plugins = g_hash_table_new_full (g_str_hash, g_str_equal,
171                                                 g_free, NULL);
172 }
173 
174 static void
peas_plugin_loader_c_finalize(GObject * object)175 peas_plugin_loader_c_finalize (GObject *object)
176 {
177   PeasPluginLoaderC *cloader = PEAS_PLUGIN_LOADER_C (object);
178   PeasPluginLoaderCPrivate *priv = GET_PRIV (cloader);
179 
180   g_mutex_clear (&priv->lock);
181 
182   g_hash_table_destroy (priv->loaded_plugins);
183 
184   G_OBJECT_CLASS (peas_plugin_loader_c_parent_class)->finalize (object);
185 }
186 
187 static void
peas_plugin_loader_c_class_init(PeasPluginLoaderCClass * klass)188 peas_plugin_loader_c_class_init (PeasPluginLoaderCClass *klass)
189 {
190   GObjectClass *object_class = G_OBJECT_CLASS (klass);
191   PeasPluginLoaderClass *loader_class = PEAS_PLUGIN_LOADER_CLASS (klass);
192 
193   quark_extension_type = g_quark_from_static_string ("peas-extension-type");
194   intern_plugin_info = g_intern_static_string ("plugin-info");
195 
196   object_class->finalize = peas_plugin_loader_c_finalize;
197 
198   loader_class->load = peas_plugin_loader_c_load;
199   loader_class->unload = peas_plugin_loader_c_unload;
200   loader_class->provides_extension = peas_plugin_loader_c_provides_extension;
201   loader_class->create_extension = peas_plugin_loader_c_create_extension;
202 }
203 
204 /*
205  * peas_plugin_loader_c_new:
206  *
207  * Return a new instance of #PeasPluginLoaderC.
208  *
209  * Returns: a new instance of #PeasPluginLoaderC.
210  */
211 PeasPluginLoader *
peas_plugin_loader_c_new(void)212 peas_plugin_loader_c_new (void)
213 {
214   return PEAS_PLUGIN_LOADER (g_object_new (PEAS_TYPE_PLUGIN_LOADER_C, NULL));
215 }
216