1/* valatyperegisterfunction.vala
2 *
3 * Copyright (C) 2006-2010  Jürg Billeter
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Lesser General Public License for more details.
14
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18 *
19 * Author:
20 * 	Jürg Billeter <j@bitron.ch>
21 */
22
23using GLib;
24
25/**
26 * C function to register a type at runtime.
27 */
28public abstract class Vala.TypeRegisterFunction {
29	CCodeFragment source_declaration_fragment = new CCodeFragment ();
30	CCodeFragment declaration_fragment = new CCodeFragment ();
31	CCodeFragment definition_fragment = new CCodeFragment ();
32
33	/**
34	 * Constructs the C function from the specified type.
35	 */
36	public void init_from_type (CodeContext context, bool plugin, bool declaration_only) {
37		var type_symbol = get_type_declaration ();
38
39		bool fundamental = false;
40		unowned Class? cl = type_symbol as Class;
41		if (cl != null && !cl.is_compact && cl.base_class == null) {
42			fundamental = true;
43		}
44
45		string type_id_name = "%s_type_id".printf (get_ccode_lower_case_name (type_symbol));
46
47		var type_block = new CCodeBlock ();
48		var type_once_block = new CCodeBlock ();
49		CCodeDeclaration cdecl;
50		if (!plugin) {
51			cdecl = new CCodeDeclaration ("gsize");
52			cdecl.add_declarator (new CCodeVariableDeclarator (type_id_name + "__volatile", new CCodeConstant ("0")));
53			cdecl.modifiers = CCodeModifiers.STATIC | CCodeModifiers.VOLATILE;
54			type_block.add_statement (cdecl);
55		} else {
56			cdecl = new CCodeDeclaration ("GType");
57			cdecl.add_declarator (new CCodeVariableDeclarator (type_id_name, new CCodeConstant ("0")));
58			cdecl.modifiers = CCodeModifiers.STATIC;
59			source_declaration_fragment.append (cdecl);
60		}
61
62		CCodeFunction fun;
63		CCodeFunction fun_once = null;
64		if (!plugin) {
65			fun = new CCodeFunction (get_ccode_type_function (type_symbol), "GType");
66			fun.modifiers = CCodeModifiers.CONST;
67
68			/* Function will not be prototyped anyway */
69			if (get_accessibility () == SymbolAccessibility.PRIVATE) {
70				// avoid C warning as this function is not always used
71				fun.modifiers |= CCodeModifiers.STATIC | CCodeModifiers.UNUSED;
72			} else if (context.hide_internal && get_accessibility () == SymbolAccessibility.INTERNAL) {
73				// avoid C warning as this function is not always used
74				fun.modifiers |= CCodeModifiers.INTERNAL | CCodeModifiers.UNUSED;
75			}
76
77			fun.is_declaration = true;
78			declaration_fragment.append (fun.copy ());
79			fun.is_declaration = false;
80
81			fun_once = new CCodeFunction ("%s_once".printf (fun.name), "GType");
82			fun_once.modifiers = CCodeModifiers.STATIC;
83			if (context.require_glib_version (2, 58)) {
84				fun_once.modifiers |= CCodeModifiers.NO_INLINE;
85			}
86
87			fun_once.is_declaration = true;
88			source_declaration_fragment.append (fun_once.copy ());
89			fun_once.is_declaration = false;
90		} else {
91			fun = new CCodeFunction ("%s_register_type".printf (get_ccode_lower_case_name (type_symbol)), "GType");
92			fun.add_parameter (new CCodeParameter ("module", "GTypeModule *"));
93
94			fun.is_declaration = true;
95			declaration_fragment.append (fun.copy ());
96			fun.is_declaration = false;
97
98			var get_fun = new CCodeFunction (get_ccode_type_function (type_symbol), "GType");
99			get_fun.modifiers = CCodeModifiers.CONST;
100
101			get_fun.is_declaration = true;
102			declaration_fragment.append (get_fun.copy ());
103			get_fun.is_declaration = false;
104
105			get_fun.block = new CCodeBlock ();
106			get_fun.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier (type_id_name)));
107
108			definition_fragment.append (get_fun);
109		}
110
111		string type_value_table_decl_name = null;
112		var type_init = new CCodeBlock ();
113
114		if (fundamental) {
115			var cgtypetabledecl = new CCodeDeclaration ("const GTypeValueTable");
116			cgtypetabledecl.modifiers = CCodeModifiers.STATIC;
117
118			cgtypetabledecl.add_declarator (new CCodeVariableDeclarator ( "g_define_type_value_table", new CCodeConstant ("{ %s, %s, %s, %s, \"p\", %s, \"p\", %s }".printf (get_gtype_value_table_init_function_name (), get_gtype_value_table_free_function_name (), get_gtype_value_table_copy_function_name (), get_gtype_value_table_peek_pointer_function_name (), get_gtype_value_table_collect_value_function_name (), get_gtype_value_table_lcopy_value_function_name ()))));
119			type_value_table_decl_name = "&g_define_type_value_table";
120			type_init.add_statement ( cgtypetabledecl );
121		}
122		else {
123			type_value_table_decl_name = "NULL";
124		}
125
126
127		if (type_symbol is ObjectTypeSymbol) {
128			var ctypedecl = new CCodeDeclaration ("const GTypeInfo");
129			ctypedecl.modifiers = CCodeModifiers.STATIC;
130			ctypedecl.add_declarator (new CCodeVariableDeclarator ("g_define_type_info", new CCodeConstant ("{ sizeof (%s), (GBaseInitFunc) %s, (GBaseFinalizeFunc) %s, (GClassInitFunc) %s, (GClassFinalizeFunc) %s, NULL, %s, 0, (GInstanceInitFunc) %s, %s }".printf (get_type_struct_name (), get_base_init_func_name (), (plugin) ? get_base_finalize_func_name () : "NULL", get_class_init_func_name (), get_class_finalize_func_name (), get_instance_struct_size (), get_instance_init_func_name (), type_value_table_decl_name))));
131			type_init.add_statement (ctypedecl);
132			if (fundamental) {
133				var ctypefundamentaldecl = new CCodeDeclaration ("const GTypeFundamentalInfo");
134				ctypefundamentaldecl.modifiers = CCodeModifiers.STATIC;
135				ctypefundamentaldecl.add_declarator (new CCodeVariableDeclarator ("g_define_type_fundamental_info", new CCodeConstant ("{ (G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE) }")));
136				type_init.add_statement (ctypefundamentaldecl);
137			}
138		}
139
140		type_init.add_statement (get_type_interface_init_declaration ());
141
142		CCodeFunctionCall reg_call;
143		if (type_symbol is Struct) {
144			reg_call = new CCodeFunctionCall (new CCodeIdentifier ("g_boxed_type_register_static"));
145		} else if (type_symbol is Enum) {
146			unowned Enum en = (Enum) type_symbol;
147			if (en.is_flags) {
148				reg_call = new CCodeFunctionCall (new CCodeIdentifier ("g_flags_register_static"));
149			} else {
150				reg_call = new CCodeFunctionCall (new CCodeIdentifier ("g_enum_register_static"));
151			}
152		} else if (fundamental) {
153			reg_call = new CCodeFunctionCall (new CCodeIdentifier ("g_type_register_fundamental"));
154			reg_call.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("g_type_fundamental_next")));
155		} else if (!plugin) {
156			reg_call = new CCodeFunctionCall (new CCodeIdentifier ("g_type_register_static"));
157			reg_call.add_argument (new CCodeIdentifier (get_parent_type_name ()));
158		} else {
159			reg_call = new CCodeFunctionCall (new CCodeIdentifier ("g_type_module_register_type"));
160			reg_call.add_argument (new CCodeIdentifier ("module"));
161			reg_call.add_argument (new CCodeIdentifier (get_parent_type_name ()));
162		}
163		reg_call.add_argument (new CCodeConstant ("\"%s\"".printf (get_ccode_name (type_symbol))));
164		if (type_symbol is Struct) {
165			var st = (Struct) type_symbol;
166			reg_call.add_argument (new CCodeCastExpression (new CCodeIdentifier (get_ccode_dup_function (st)), "GBoxedCopyFunc"));
167			reg_call.add_argument (new CCodeCastExpression (new CCodeIdentifier (get_ccode_free_function (st)), "GBoxedFreeFunc"));
168		} else if (type_symbol is Enum) {
169			unowned Enum en = (Enum) type_symbol;
170			var clist = new CCodeInitializerList (); /* or during visit time? */
171
172			CCodeInitializerList clist_ev = null;
173			foreach (EnumValue ev in en.get_values ()) {
174				clist_ev = new CCodeInitializerList ();
175				clist_ev.append (new CCodeConstant (get_ccode_name (ev)));
176				clist_ev.append (new CCodeConstant ("\"%s\"".printf (get_ccode_name (ev))));
177				clist_ev.append (new CCodeConstant ("\"%s\"".printf (ev.nick)));
178				clist.append (clist_ev);
179			}
180
181			clist_ev = new CCodeInitializerList ();
182			clist_ev.append (new CCodeConstant ("0"));
183			clist_ev.append (new CCodeConstant ("NULL"));
184			clist_ev.append (new CCodeConstant ("NULL"));
185			clist.append (clist_ev);
186
187			var enum_decl = new CCodeVariableDeclarator ("values[]", clist);
188
189			if (en.is_flags) {
190				cdecl = new CCodeDeclaration ("const GFlagsValue");
191			} else {
192				cdecl = new CCodeDeclaration ("const GEnumValue");
193			}
194
195			cdecl.add_declarator (enum_decl);
196			cdecl.modifiers = CCodeModifiers.STATIC;
197
198			type_init.add_statement (cdecl);
199
200			reg_call.add_argument (new CCodeIdentifier ("values"));
201		} else {
202			reg_call.add_argument (new CCodeIdentifier ("&g_define_type_info"));
203			if (fundamental) {
204				reg_call.add_argument (new CCodeIdentifier ("&g_define_type_fundamental_info"));
205			}
206			reg_call.add_argument (new CCodeConstant (get_type_flags ()));
207		}
208
209		var once_call_block = new CCodeBlock ();
210		if (!plugin) {
211			var temp_decl = new CCodeDeclaration ("GType");
212			temp_decl.add_declarator (new CCodeVariableDeclarator (type_id_name, reg_call));
213			type_init.add_statement (temp_decl);
214			temp_decl = new CCodeDeclaration ("GType");
215			temp_decl.add_declarator (new CCodeVariableDeclarator (type_id_name, new CCodeFunctionCall (new CCodeIdentifier (fun_once.name))));
216			once_call_block.add_statement (temp_decl);
217		} else {
218			type_init.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier (type_id_name), reg_call)));
219		}
220
221		 if (cl != null && cl.has_class_private_fields) {
222			CCodeFunctionCall add_class_private_call;
223
224			add_class_private_call = new CCodeFunctionCall (new CCodeIdentifier ("g_type_add_class_private"));
225			add_class_private_call.add_argument (new CCodeIdentifier (type_id_name));
226			add_class_private_call.add_argument (new CCodeIdentifier ("sizeof (%sPrivate)".printf (get_ccode_type_name (cl))));
227			type_init.add_statement (new CCodeExpressionStatement (add_class_private_call));
228		}
229
230		if (!declaration_only) {
231			get_type_interface_init_statements (context, type_init, plugin);
232		}
233
234		if (cl != null && (cl.has_private_fields || cl.has_type_parameters ())) {
235			if (!plugin) {
236				var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_type_add_instance_private"));
237				ccall.add_argument (new CCodeIdentifier (type_id_name));
238				ccall.add_argument (new CCodeIdentifier ("sizeof (%sPrivate)".printf (get_ccode_name (cl))));
239				type_init.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("%s_private_offset".printf (get_ccode_name (cl))), ccall)));
240			} else {
241				type_init.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("%s_private_offset".printf (get_ccode_name (cl))), new CCodeIdentifier ("sizeof (%sPrivate)".printf (get_ccode_name (cl))))));
242			}
243		}
244
245		if (!plugin) {
246			// the condition that guards the type initialisation
247			var enter = new CCodeFunctionCall (new CCodeIdentifier ("g_once_init_enter"));
248			enter.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (type_id_name + "__volatile")));
249
250			var leave = new CCodeFunctionCall (new CCodeIdentifier ("g_once_init_leave"));
251			leave.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (type_id_name + "__volatile")));
252			leave.add_argument (new CCodeIdentifier (type_id_name));
253			once_call_block.add_statement (new CCodeExpressionStatement (leave));
254
255			var cif = new CCodeIfStatement (enter, once_call_block);
256			type_block.add_statement (cif);
257			type_block.add_statement (new CCodeReturnStatement (new CCodeIdentifier (type_id_name + "__volatile")));
258
259			type_once_block = type_init;
260			type_once_block.add_statement (new CCodeReturnStatement (new CCodeIdentifier (type_id_name)));
261		} else {
262			type_block = type_init;
263			type_block.add_statement (new CCodeReturnStatement (new CCodeIdentifier (type_id_name)));
264		}
265
266		if (!plugin) {
267			fun_once.block = type_once_block;
268			definition_fragment.append (fun_once);
269		}
270
271		fun.block = type_block;
272		definition_fragment.append (fun);
273	}
274
275	/**
276	 * Returns the data type to be registered.
277	 *
278	 * @return type to be registered
279	 */
280	public abstract TypeSymbol get_type_declaration ();
281
282	/**
283	 * Returns the name of the type struct in C code.
284	 *
285	 * @return C struct name
286	 */
287	public virtual string get_type_struct_name () {
288		assert_not_reached ();
289	}
290	/**
291	 * Returns the name of the base_init function in C code.
292	 *
293	 * @return C function name
294	 */
295	public virtual string get_base_init_func_name () {
296		assert_not_reached ();
297	}
298
299	/**
300	 * Returns the name of the class_finalize function in C code.
301	 *
302	 * @return C function name
303	 */
304	public virtual string get_class_finalize_func_name () {
305		assert_not_reached ();
306	}
307
308	/**
309	 * Returns the name of the base_finalize function in C code.
310	 *
311	 * @return C function name
312	 */
313	public virtual string get_base_finalize_func_name () {
314		assert_not_reached ();
315	}
316
317	/**
318	 * Returns the name of the class_init function in C code.
319	 *
320	 * @return C function name
321	 */
322	public virtual string get_class_init_func_name () {
323		assert_not_reached ();
324	}
325
326	/**
327	 * Returns the size of the instance struct in C code.
328	 *
329	 * @return C instance struct size
330	 */
331	public virtual string get_instance_struct_size () {
332		assert_not_reached ();
333	}
334
335	/**
336	 * Returns the name of the instance_init function in C code.
337	 *
338	 * @return C function name
339	 */
340	public virtual string get_instance_init_func_name () {
341		assert_not_reached ();
342	}
343
344	/**
345	 * Returns the name of the parent type in C code.
346	 *
347	 * @return C function name
348	 */
349	public virtual string get_parent_type_name () {
350		assert_not_reached ();
351	}
352
353
354
355	/**
356	 * Returns the C-name of the new generated GTypeValueTable init function or null when not available.
357	 *
358	 * @return C function name
359	 */
360	public virtual string? get_gtype_value_table_init_function_name () {
361		return null;
362	}
363
364	/**
365	 * Returns the C-name of the new generated GTypeValueTable peek pointer function or null when not available.
366	 *
367	 * @return C function name
368	 */
369	public virtual string? get_gtype_value_table_peek_pointer_function_name () {
370		return null;
371	}
372
373	/**
374	 * Returns the C-name of the new generated GTypeValueTable free function or null when not available.
375	 *
376	 * @return C function name
377	 */
378	public virtual string? get_gtype_value_table_free_function_name () {
379		return null;
380	}
381
382	/**
383	 * Returns the C-name of the new generated GTypeValueTable copy function or null when not available.
384	 *
385	 * @return C function name
386	 */
387	public virtual string? get_gtype_value_table_copy_function_name () {
388		return null;
389	}
390
391	/**
392	 * Returns the C-name of the new generated GTypeValueTable lcopy function or null when not available.
393	 *
394	 * @return C function name
395	 */
396	public virtual string? get_gtype_value_table_lcopy_value_function_name () {
397		return null;
398	}
399
400	/**
401	 * Returns the C-name of the new generated GTypeValueTable collect value function or null when not available.
402	 *
403	 * @return C function name
404	 */
405	public virtual string? get_gtype_value_table_collect_value_function_name () {
406		return null;
407	}
408
409	/**
410	 * Returns the set of type flags to be applied when registering.
411	 *
412	 * @return type flags
413	 */
414	public virtual string get_type_flags () {
415		return "0";
416	}
417
418	/**
419	 * Returns additional C declarations to setup interfaces.
420	 *
421	 * @return C declarations
422	 */
423	public virtual CCodeFragment get_type_interface_init_declaration () {
424		return new CCodeFragment ();
425	}
426
427	/**
428	 * Returns additional C initialization statements to setup interfaces.
429	 */
430	public virtual void get_type_interface_init_statements (CodeContext context, CCodeBlock block, bool plugin) {
431	}
432
433	public CCodeFragment get_source_declaration () {
434		return source_declaration_fragment;
435	}
436
437	/**
438	 * Returns the declaration for this type register function in C code.
439	 *
440	 * @return C function declaration fragment
441	 */
442	public CCodeFragment get_declaration () {
443		return declaration_fragment;
444	}
445
446	/**
447	 * Returns the definition for this type register function in C code.
448	 *
449	 * @return C function definition fragment
450	 */
451	public CCodeFragment get_definition () {
452		return definition_fragment;
453	}
454
455	/**
456	 * Returns the accessibility for this type.
457	 */
458	public abstract SymbolAccessibility get_accessibility ();
459}
460