Add support for async signal handlers
[vala-lang.git] / vala / valasignal.vala
blob3c8187051a1d117a7a11ca2eedfd839209d15d89
1 /* valasignal.vala
3 * Copyright (C) 2006-2011 Jürg Billeter
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.
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.
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
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
23 using GLib;
25 /**
26 * Represents an object signal. Signals enable objects to provide notifications.
28 public class Vala.Signal : Symbol, Lockable {
29 /**
30 * The return type of handlers of this signal.
32 public DataType return_type {
33 get { return _return_type; }
34 set {
35 _return_type = value;
36 _return_type.parent_node = this;
40 public Block body {
41 get { return _body; }
42 set {
43 _body = value;
44 if (_body != null) {
45 _body.owner = scope;
50 /**
51 * Specifies whether this signal has an emitter wrapper function.
53 public bool has_emitter { get; set; }
55 /**
56 * Specifies whether this signal has virtual method handler.
58 public bool is_virtual { get; set; }
60 private List<Parameter> parameters = new ArrayList<Parameter> ();
61 /**
62 * Refers to the default signal handler, which is an anonymous
63 * function in the scope.
64 * */
65 public Method default_handler { get; private set; }
67 public bool is_detailed { get; set; }
69 public bool no_recurse { get; set; }
71 public string run_type { get; set; }
73 public bool is_action { get; set; }
75 public bool no_hooks { get; set; }
78 private string cname;
80 private bool lock_used = false;
82 private DataType _return_type;
84 private Block _body;
86 /**
87 * Creates a new signal.
89 * @param name signal name
90 * @param return_type signal return type
91 * @param source reference to source code
92 * @return newly created signal
94 public Signal (string name, DataType return_type, SourceReference? source_reference = null, Comment? comment = null) {
95 base (name, source_reference, comment);
96 this.return_type = return_type;
97 this.run_type = "last";
101 * Appends parameter to signal handler.
103 * @param param a formal parameter
105 public void add_parameter (Parameter param) {
106 // default C parameter position
107 param.cparameter_position = parameters.size + 1;
108 param.carray_length_parameter_position = param.cparameter_position + 0.1;
109 param.cdelegate_target_parameter_position = param.cparameter_position + 0.1;
110 param.cdestroy_notify_parameter_position = param.cparameter_position + 0.1;
112 parameters.add (param);
113 scope.add (param.name, param);
116 public List<Parameter> get_parameters () {
117 return parameters;
121 * Returns generated delegate to be used for signal handlers.
123 * @return delegate
125 public Delegate get_delegate (DataType sender_type, CodeNode node_reference) {
126 var actual_return_type = return_type.get_actual_type (sender_type, null, node_reference);
128 var generated_delegate = new Delegate (null, actual_return_type);
129 generated_delegate.has_target = true;
130 generated_delegate.access = SymbolAccessibility.PUBLIC;
131 generated_delegate.owner = scope;
133 // sender parameter is never null and doesn't own its value
134 var sender_param_type = sender_type.copy ();
135 sender_param_type.value_owned = false;
136 sender_param_type.nullable = false;
138 generated_delegate.sender_type = sender_param_type;
140 bool is_generic = false;
142 foreach (Parameter param in parameters) {
143 var actual_param = param.copy ();
144 actual_param.variable_type = actual_param.variable_type.get_actual_type (sender_type, null, node_reference);
145 generated_delegate.add_parameter (actual_param);
147 if (actual_param.variable_type is GenericType) {
148 is_generic = true;
152 if (is_generic) {
153 var cl = (ObjectTypeSymbol) parent_symbol;
154 foreach (var type_param in cl.get_type_parameters ()) {
155 generated_delegate.add_type_parameter (new TypeParameter (type_param.name, type_param.source_reference));
158 // parameter types must refer to the delegate type parameters
159 // instead of to the class type parameters
160 foreach (var param in generated_delegate.get_parameters ()) {
161 if (param.variable_type is GenericType) {
162 param.variable_type.type_parameter = generated_delegate.get_type_parameters ().get (generated_delegate.get_type_parameter_index (param.variable_type.type_parameter.name));
167 scope.add (null, generated_delegate);
169 return generated_delegate;
173 * Returns the name of this signal as it is used in C code.
175 * @return the name to be used in C code
177 public string get_cname () {
178 if (cname == null) {
179 cname = camel_case_to_lower_case (name);
181 return cname;
184 public void set_cname (string cname) {
185 this.cname = cname;
189 * Returns the string literal of this signal to be used in C code.
191 * @return string literal to be used in C code
193 public CCodeConstant get_canonical_cconstant (string? detail = null) {
194 var str = new StringBuilder ("\"");
196 string i = get_cname ();
198 while (i.length > 0) {
199 unichar c = i.get_char ();
200 if (c == '_') {
201 str.append_c ('-');
202 } else {
203 str.append_unichar (c);
206 i = i.next_char ();
209 if (detail != null) {
210 str.append ("::");
211 str.append (detail);
214 str.append_c ('"');
216 return new CCodeConstant (str.str);
219 public override void accept (CodeVisitor visitor) {
220 visitor.visit_signal (this);
223 public override void accept_children (CodeVisitor visitor) {
224 return_type.accept (visitor);
226 foreach (Parameter param in parameters) {
227 param.accept (visitor);
229 if (default_handler == null && body != null) {
230 body.accept (visitor);
231 } else if (default_handler != null) {
232 default_handler.accept (visitor);
236 void process_signal_attribute (Attribute a) {
237 if (a.has_argument ("detailed")) {
238 is_detailed = a.get_bool ("detailed");
240 if (a.has_argument ("no_recurse")) {
241 no_recurse = a.get_bool ("no_recurse");
243 if (a.has_argument ("run")) {
244 var arg = a.get_string ("run");
245 if (arg == "first") {
246 run_type = "first";
247 } else if (arg == "last") {
248 run_type = "last";
249 } else if (arg == "cleanup") {
250 run_type = "cleanup";
254 if (a.has_argument ("action")) {
255 is_action = a.get_bool ("action");
258 if (a.has_argument ("no_hooks")) {
259 no_hooks = a.get_bool ("no_hooks");
264 * Process all associated attributes.
266 public void process_attributes () {
267 foreach (Attribute a in attributes) {
268 if (a.name == "HasEmitter") {
269 has_emitter = true;
270 } else if (a.name == "Signal") {
271 process_signal_attribute (a);
272 } else if (a.name == "Deprecated") {
273 process_deprecated_attribute (a);
278 public bool get_lock_used () {
279 return lock_used;
282 public void set_lock_used (bool used) {
283 lock_used = used;
286 public override void replace_type (DataType old_type, DataType new_type) {
287 if (return_type == old_type) {
288 return_type = new_type;
292 public override bool check (CodeContext context) {
293 if (checked) {
294 return !error;
297 checked = true;
299 process_attributes ();
301 return_type.check (context);
303 foreach (Parameter param in parameters) {
304 param.check (context);
307 if (!is_virtual && body != null) {
308 Report.error (source_reference, "Only virtual signals can have a default signal handler body");
312 if (is_virtual) {
313 default_handler = new Method (name, return_type, source_reference);
315 default_handler.owner = owner;
316 default_handler.access = access;
317 default_handler.external = external;
318 default_handler.is_virtual = true;
319 default_handler.vfunc_name = name;
320 default_handler.signal_reference = this;
321 default_handler.body = body;
324 foreach (Parameter param in parameters) {
325 default_handler.add_parameter (param);
328 var cl = parent_symbol as ObjectTypeSymbol;
330 cl.add_hidden_method (default_handler);
331 default_handler.check (context);
335 if (!external_package && !hides && get_hidden_member () != null) {
336 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 ()));
339 return !error;