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