1/* valasignal.vala
2 *
3 * Copyright (C) 2006-2012  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 * Represents an object signal. Signals enable objects to provide notifications.
27 */
28public class Vala.Signal : Symbol, Callable {
29	/**
30	 * The return type of handlers of this signal.
31	 */
32	public DataType return_type {
33		get { return _return_type; }
34		set {
35			_return_type = value;
36			_return_type.parent_node = this;
37		}
38	}
39
40	public Block body {
41		get { return _body; }
42		set {
43			_body = value;
44			if (_body != null) {
45				_body.owner = scope;
46			}
47		}
48	}
49
50	/**
51	 * Specifies whether this signal has virtual method handler.
52	 */
53	public bool is_virtual { get; set; }
54
55	private List<Parameter> parameters = new ArrayList<Parameter> ();
56	/**
57	 * Refers to the default signal handler, which is an anonymous
58	 * function in the scope.
59	 * */
60	public Method default_handler { get; private set; }
61
62	/**
63	 * Refers to the public signal emitter method, which is an anonymous
64	 * function in the scope.
65	 * */
66	public Method emitter { get; private set; }
67
68	private DataType _return_type;
69
70	private Block _body;
71
72	/**
73	 * Creates a new signal.
74	 *
75	 * @param name              signal name
76	 * @param return_type       signal return type
77	 * @param source_reference  reference to source code
78	 * @return                  newly created signal
79	 */
80	public Signal (string name, DataType return_type, SourceReference? source_reference = null, Comment? comment = null) {
81		base (name, source_reference, comment);
82		this.return_type = return_type;
83	}
84
85	/**
86	 * Appends parameter to signal handler.
87	 *
88	 * @param param a formal parameter
89	 */
90	public void add_parameter (Parameter param) {
91		parameters.add (param);
92		scope.add (param.name, param);
93	}
94
95	public unowned List<Parameter> get_parameters () {
96		return parameters;
97	}
98
99	/**
100	 * Returns generated delegate to be used for signal handlers.
101	 *
102	 * @return delegate
103	 */
104	public Delegate get_delegate (DataType sender_type, CodeNode node_reference) {
105		var actual_return_type = return_type.get_actual_type (sender_type, null, node_reference);
106
107		var generated_delegate = new Delegate (null, actual_return_type);
108		generated_delegate.access = SymbolAccessibility.PUBLIC;
109		generated_delegate.owner = scope;
110
111		// sender parameter is never null and doesn't own its value
112		var sender_param_type = sender_type.copy ();
113		sender_param_type.value_owned = false;
114		sender_param_type.nullable = false;
115
116		generated_delegate.sender_type = sender_param_type;
117
118		bool is_generic = false;
119
120		foreach (Parameter param in parameters) {
121			var actual_param = param.copy ();
122			actual_param.variable_type = actual_param.variable_type.get_actual_type (sender_type, null, node_reference);
123			generated_delegate.add_parameter (actual_param);
124
125			if (actual_param.variable_type.is_generic ()) {
126				is_generic = true;
127			}
128		}
129
130		if (is_generic) {
131			unowned ObjectTypeSymbol cl = (ObjectTypeSymbol) parent_symbol;
132			foreach (var type_param in cl.get_type_parameters ()) {
133				generated_delegate.add_type_parameter (new TypeParameter (type_param.name, type_param.source_reference));
134			}
135
136			// parameter types must refer to the delegate type parameters
137			// instead of to the class type parameters
138			foreach (var param in generated_delegate.get_parameters ()) {
139				foreach (var type_param in generated_delegate.get_type_parameters ()) {
140					param.variable_type.replace_type_parameter (cl.get_type_parameters ().get (cl.get_type_parameter_index (type_param.name)), type_param);
141				}
142			}
143		}
144
145		scope.add (null, generated_delegate);
146
147		return generated_delegate;
148	}
149
150	public override void accept (CodeVisitor visitor) {
151		visitor.visit_signal (this);
152	}
153
154	public override void accept_children (CodeVisitor visitor) {
155		return_type.accept (visitor);
156
157		foreach (Parameter param in parameters) {
158			param.accept (visitor);
159		}
160		if (default_handler == null && body != null) {
161			body.accept (visitor);
162		} else if (default_handler != null) {
163			default_handler.accept (visitor);
164		}
165		if (emitter != null) {
166			emitter.accept (visitor);
167		}
168	}
169
170	public override void replace_type (DataType old_type, DataType new_type) {
171		if (return_type == old_type) {
172			return_type = new_type;
173		}
174	}
175
176	public override bool check (CodeContext context) {
177		if (checked) {
178			return !error;
179		}
180
181		checked = true;
182
183		// parent_symbol may be null for dynamic signals
184		unowned Class? parent_cl = parent_symbol as Class;
185		if (parent_cl != null && parent_cl.is_compact) {
186			error = true;
187			Report.error (source_reference, "Signals are not supported in compact classes");
188			return false;
189		}
190
191		if (parent_cl != null) {
192			foreach (DataType base_type in parent_cl.get_base_types ()) {
193				if (SemanticAnalyzer.symbol_lookup_inherited (base_type.type_symbol, name) is Signal) {
194					error = true;
195					Report.error (source_reference, "Signals with the same name as a signal in a base type are not supported");
196					return false;
197				}
198			}
199		}
200
201		if (this is DynamicSignal) {
202			return !error;
203		}
204
205		return_type.check (context);
206
207		if (return_type.type_symbol == context.analyzer.va_list_type.type_symbol) {
208			error = true;
209			Report.error (source_reference, "`%s' not supported as return type".printf (return_type.type_symbol.get_full_name ()));
210			return false;
211		}
212
213		foreach (Parameter param in parameters) {
214			if (param.ellipsis) {
215				Report.error  (param.source_reference, "Signals with variable argument lists are not supported");
216				return false;
217			}
218
219			if (!param.check (context)) {
220				error = true;
221			}
222		}
223
224		if (!is_virtual && body != null) {
225			error = true;
226			Report.error (source_reference, "Only virtual signals can have a default signal handler body");
227		}
228
229
230		if (is_virtual) {
231			default_handler = new Method (name, return_type, source_reference);
232
233			default_handler.owner = owner;
234			default_handler.access = access;
235			default_handler.external = external;
236			default_handler.hides = hides;
237			default_handler.is_virtual = true;
238			default_handler.signal_reference = this;
239			default_handler.body = body;
240
241
242			foreach (Parameter param in parameters) {
243				default_handler.add_parameter (param);
244			}
245
246			unowned ObjectTypeSymbol? cl = parent_symbol as ObjectTypeSymbol;
247
248			cl.add_hidden_method (default_handler);
249			default_handler.check (context);
250		}
251
252		if (get_attribute ("HasEmitter") != null) {
253			emitter = new Method (name, return_type, source_reference);
254
255			emitter.owner = owner;
256			emitter.access = access;
257
258			var body = new Block (source_reference);
259			var call = new MethodCall (new MemberAccess.simple (name, source_reference), source_reference);
260
261			foreach (Parameter param in parameters) {
262				emitter.add_parameter (param);
263				call.add_argument (new MemberAccess.simple (param.name, source_reference));
264			}
265
266			if (return_type is VoidType) {
267				body.add_statement (new ExpressionStatement (call, source_reference));
268			} else {
269				body.add_statement (new ReturnStatement (call, source_reference));
270			}
271			emitter.body = body;
272
273			unowned ObjectTypeSymbol? cl = parent_symbol as ObjectTypeSymbol;
274
275			cl.add_hidden_method (emitter);
276			if (!external_package) {
277				emitter.check (context);
278			}
279		}
280
281
282		if (!external_package && !hides && get_hidden_member () != null) {
283			Report.warning (source_reference, "%s hides inherited signal `%s'. Use the `new' keyword if hiding was intentional".printf (get_full_name (), get_hidden_member ().get_full_name ()));
284		}
285
286		return !error;
287	}
288}
289
290