1/* valaccodedelegatemodule.vala 2 * 3 * Copyright (C) 2006-2010 Jürg Billeter 4 * Copyright (C) 2006-2008 Raffaele Sandrini 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * 20 * Author: 21 * Jürg Billeter <j@bitron.ch> 22 * Raffaele Sandrini <raffaele@sandrini.ch> 23 */ 24 25 26/** 27 * The link between an assignment and generated code. 28 */ 29public class Vala.CCodeDelegateModule : CCodeArrayModule { 30 public override void generate_delegate_declaration (Delegate d, CCodeFile decl_space) { 31 if (add_symbol_declaration (decl_space, d, get_ccode_name (d))) { 32 return; 33 } 34 35 // internally generated delegates don't require a typedef 36 if (d.sender_type != null) { 37 return; 38 } 39 40 var creturn_type = get_callable_creturn_type (d); 41 if (creturn_type is DelegateType && ((DelegateType) creturn_type).delegate_symbol == d) { 42 // recursive delegate 43 creturn_type = new DelegateType ((Delegate) context.root.scope.lookup ("GLib").scope.lookup ("Callback")); 44 } 45 46 generate_type_declaration (creturn_type, decl_space); 47 48 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal); 49 50 var cfundecl = new CCodeFunctionDeclarator (get_ccode_name (d)); 51 foreach (Parameter param in d.get_parameters ()) { 52 generate_parameter (param, decl_space, cparam_map, null); 53 } 54 55 // FIXME partial code duplication with CCodeMethodModule.generate_cparameters 56 57 if (d.return_type.is_real_non_null_struct_type ()) { 58 // structs are returned via out parameter 59 var cparam = new CCodeParameter ("result", get_ccode_name (d.return_type) + "*"); 60 cparam_map.set (get_param_pos (-3), cparam); 61 } else if (get_ccode_array_length (d) && d.return_type is ArrayType) { 62 // return array length if appropriate 63 var array_type = (ArrayType) d.return_type; 64 var length_ctype = get_ccode_array_length_type (d) + "*"; 65 66 for (int dim = 1; dim <= array_type.rank; dim++) { 67 var cparam = new CCodeParameter (get_array_length_cname ("result", dim), length_ctype); 68 cparam_map.set (get_param_pos (get_ccode_array_length_pos (d) + 0.01 * dim), cparam); 69 } 70 } else if (get_ccode_delegate_target (d) && d.return_type is DelegateType) { 71 // return delegate target if appropriate 72 var deleg_type = (DelegateType) d.return_type; 73 if (deleg_type.delegate_symbol.has_target) { 74 generate_type_declaration (delegate_target_type, decl_space); 75 var cparam = new CCodeParameter (get_delegate_target_cname ("result"), get_ccode_name (delegate_target_type) + "*"); 76 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (d)), cparam); 77 if (deleg_type.is_disposable ()) { 78 generate_type_declaration (delegate_target_destroy_type, decl_space); 79 cparam = new CCodeParameter (get_delegate_target_destroy_notify_cname ("result"), get_ccode_name (delegate_target_destroy_type) + "*"); 80 cparam_map.set (get_param_pos (get_ccode_destroy_notify_pos (d)), cparam); 81 } 82 } 83 } 84 85 if (d.has_target) { 86 generate_type_declaration (delegate_target_type, decl_space); 87 var cparam = new CCodeParameter ("user_data", get_ccode_name (delegate_target_type)); 88 cparam_map.set (get_param_pos (get_ccode_instance_pos (d)), cparam); 89 } 90 if (d.tree_can_fail) { 91 generate_type_declaration (gerror_type, decl_space); 92 var cparam = new CCodeParameter ("error", "GError**"); 93 cparam_map.set (get_param_pos (get_ccode_error_pos (d)), cparam); 94 } 95 96 // append C parameters in the right order 97 int last_pos = -1; 98 int min_pos; 99 while (true) { 100 min_pos = -1; 101 foreach (int pos in cparam_map.get_keys ()) { 102 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { 103 min_pos = pos; 104 } 105 } 106 if (min_pos == -1) { 107 break; 108 } 109 cfundecl.add_parameter (cparam_map.get (min_pos)); 110 last_pos = min_pos; 111 } 112 113 var ctypedef = new CCodeTypeDefinition (get_ccode_name (creturn_type), cfundecl); 114 115 if (d.version.deprecated) { 116 if (context.profile == Profile.GOBJECT) { 117 decl_space.add_include ("glib.h"); 118 } 119 ctypedef.modifiers |= CCodeModifiers.DEPRECATED; 120 } 121 122 decl_space.add_type_declaration (ctypedef); 123 } 124 125 public override void visit_delegate (Delegate d) { 126 generate_delegate_declaration (d, cfile); 127 128 if (!d.is_internal_symbol ()) { 129 generate_delegate_declaration (d, header_file); 130 } 131 if (!d.is_private_symbol ()) { 132 generate_delegate_declaration (d, internal_header_file); 133 } 134 135 d.accept_children (this); 136 } 137 138 public override string get_delegate_target_cname (string delegate_cname) { 139 return "%s_target".printf (delegate_cname); 140 } 141 142 public override CCodeExpression get_delegate_target_cexpression (Expression delegate_expr, out CCodeExpression delegate_target_destroy_notify) { 143 delegate_target_destroy_notify = get_delegate_target_destroy_notify_cvalue (delegate_expr.target_value); 144 return get_delegate_target_cvalue (delegate_expr.target_value); 145 } 146 147 public override CCodeExpression get_delegate_target_cvalue (TargetValue value) { 148 return ((GLibValue) value).delegate_target_cvalue; 149 } 150 151 public override CCodeExpression get_delegate_target_destroy_notify_cvalue (TargetValue value) { 152 return ((GLibValue) value).delegate_target_destroy_notify_cvalue; 153 } 154 155 public override string get_delegate_target_destroy_notify_cname (string delegate_cname) { 156 return "%s_target_destroy_notify".printf (delegate_cname); 157 } 158 159 public override CCodeExpression get_implicit_cast_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, CodeNode? node) { 160 if (target_type is DelegateType && expression_type is MethodType) { 161 var dt = (DelegateType) target_type; 162 var mt = (MethodType) expression_type; 163 164 var method = mt.method_symbol; 165 if (method.base_method != null) { 166 method = method.base_method; 167 } else if (method.base_interface_method != null) { 168 method = method.base_interface_method; 169 } 170 171 return new CCodeIdentifier (generate_delegate_wrapper (method, dt, node)); 172 } 173 174 return base.get_implicit_cast_expression (source_cexpr, expression_type, target_type, node); 175 } 176 177 public string generate_delegate_wrapper (Method m, DelegateType dt, CodeNode? node) { 178 var d = dt.delegate_symbol; 179 string delegate_name; 180 var sig = d.parent_symbol as Signal; 181 var dynamic_sig = sig as DynamicSignal; 182 if (dynamic_sig != null) { 183 delegate_name = get_dynamic_signal_cname (dynamic_sig); 184 } else if (sig != null) { 185 delegate_name = get_ccode_lower_case_prefix (sig.parent_symbol) + get_ccode_lower_case_name (sig); 186 } else { 187 delegate_name = Symbol.camel_case_to_lower_case (get_ccode_name (d)); 188 } 189 190 string wrapper_name = "_%s_%s".printf (get_ccode_name (m), delegate_name); 191 192 if (!add_wrapper (wrapper_name)) { 193 // wrapper already defined 194 return wrapper_name; 195 } 196 197 // declaration 198 var creturn_type = get_callable_creturn_type (d); 199 200 var function = new CCodeFunction (wrapper_name, get_ccode_name (creturn_type)); 201 function.modifiers = CCodeModifiers.STATIC; 202 203 push_function (function); 204 205 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal); 206 207 if (d.has_target) { 208 var cparam = new CCodeParameter ("self", get_ccode_name (delegate_target_type)); 209 cparam_map.set (get_param_pos (get_ccode_instance_pos (d)), cparam); 210 } 211 212 if (d.sender_type != null) { 213 var param = new Parameter ("_sender", d.sender_type); 214 generate_parameter (param, cfile, cparam_map, null); 215 } 216 217 var d_params = d.get_parameters (); 218 foreach (Parameter param in d_params) { 219 if (dynamic_sig != null 220 && param.variable_type is ArrayType 221 && ((ArrayType) param.variable_type).element_type.type_symbol == string_type.type_symbol) { 222 // use null-terminated string arrays for dynamic signals for compatibility reasons 223 param.set_attribute_bool ("CCode", "array_length", false); 224 param.set_attribute_bool ("CCode", "array_null_terminated", true); 225 } 226 227 generate_parameter (param, cfile, cparam_map, null); 228 } 229 if (get_ccode_array_length (d) && d.return_type is ArrayType) { 230 // return array length if appropriate 231 var array_type = (ArrayType) d.return_type; 232 var length_ctype = get_ccode_array_length_type (d) + "*"; 233 234 for (int dim = 1; dim <= array_type.rank; dim++) { 235 var cparam = new CCodeParameter (get_array_length_cname ("result", dim), length_ctype); 236 cparam_map.set (get_param_pos (get_ccode_array_length_pos (d) + 0.01 * dim), cparam); 237 } 238 } else if (d.return_type is DelegateType) { 239 // return delegate target if appropriate 240 var deleg_type = (DelegateType) d.return_type; 241 242 if (get_ccode_delegate_target (d) && deleg_type.delegate_symbol.has_target) { 243 var cparam = new CCodeParameter (get_delegate_target_cname ("result"), get_ccode_name (delegate_target_type) + "*"); 244 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (d)), cparam); 245 if (deleg_type.is_disposable ()) { 246 cparam = new CCodeParameter (get_delegate_target_destroy_notify_cname ("result"), get_ccode_name (delegate_target_destroy_type) + "*"); 247 cparam_map.set (get_param_pos (get_ccode_destroy_notify_pos (d)), cparam); 248 } 249 } 250 } else if (d.return_type.is_real_non_null_struct_type ()) { 251 var cparam = new CCodeParameter ("result", "%s*".printf (get_ccode_name (d.return_type))); 252 cparam_map.set (get_param_pos (-3), cparam); 253 } 254 255 if (m.tree_can_fail) { 256 var cparam = new CCodeParameter ("error", "GError**"); 257 cparam_map.set (get_param_pos (get_ccode_error_pos (d)), cparam); 258 } 259 260 // append C parameters in the right order 261 int last_pos = -1; 262 int min_pos; 263 while (true) { 264 min_pos = -1; 265 foreach (int pos in cparam_map.get_keys ()) { 266 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { 267 min_pos = pos; 268 } 269 } 270 if (min_pos == -1) { 271 break; 272 } 273 function.add_parameter (cparam_map.get (min_pos)); 274 last_pos = min_pos; 275 } 276 277 278 // definition 279 280 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal); 281 282 int i = 0; 283 if (m.binding == MemberBinding.INSTANCE || m.closure) { 284 CCodeExpression arg; 285 if (d.has_target) { 286 arg = new CCodeIdentifier ("self"); 287 if (!m.closure && m.this_parameter != null) { 288 arg = convert_from_generic_pointer (arg, m.this_parameter.variable_type); 289 } 290 } else { 291 // use first delegate parameter as instance 292 if (d_params.size == 0 || m.closure) { 293 Report.error (node != null ? node.source_reference : null, "internal: Cannot create delegate wrapper"); 294 arg = new CCodeInvalidExpression (); 295 } else { 296 arg = new CCodeIdentifier (get_ccode_name (d_params.get (0))); 297 i = 1; 298 } 299 } 300 carg_map.set (get_param_pos (get_ccode_instance_pos (m)), arg); 301 } 302 303 bool first = true; 304 305 foreach (Parameter param in m.get_parameters ()) { 306 if (first && d.sender_type != null && m.get_parameters ().size == d.get_parameters ().size + 1) { 307 // sender parameter 308 carg_map.set (get_param_pos (get_ccode_pos (param)), new CCodeIdentifier ("_sender")); 309 310 first = false; 311 continue; 312 } 313 314 CCodeExpression arg; 315 arg = new CCodeIdentifier (get_ccode_name (d_params.get (i))); 316 if (d_params.get (i).variable_type is GenericType) { 317 arg = convert_from_generic_pointer (arg, param.variable_type); 318 } 319 carg_map.set (get_param_pos (get_ccode_pos (param)), arg); 320 321 // handle array arguments 322 if (get_ccode_array_length (param) && param.variable_type is ArrayType) { 323 var array_type = (ArrayType) param.variable_type; 324 for (int dim = 1; dim <= array_type.rank; dim++) { 325 CCodeExpression clength; 326 if (get_ccode_array_null_terminated (d_params.get (i))) { 327 requires_array_length = true; 328 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_length")); 329 len_call.add_argument (new CCodeIdentifier (d_params.get (i).name)); 330 clength = len_call; 331 } else if (!get_ccode_array_length (d_params.get (i))) { 332 clength = new CCodeConstant ("-1"); 333 } else { 334 clength = new CCodeIdentifier (get_variable_array_length_cname (d_params.get (i), dim)); 335 } 336 carg_map.set (get_param_pos (get_ccode_array_length_pos (param) + 0.01 * dim), clength); 337 } 338 } else if (get_ccode_delegate_target (param) && param.variable_type is DelegateType) { 339 var deleg_type = (DelegateType) param.variable_type; 340 341 if (deleg_type.delegate_symbol.has_target) { 342 var ctarget = new CCodeIdentifier (get_ccode_delegate_target_name (d_params.get (i))); 343 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), ctarget); 344 if (deleg_type.is_disposable ()) { 345 var ctarget_destroy_notify = new CCodeIdentifier (get_ccode_delegate_target_destroy_notify_name (d_params.get (i))); 346 carg_map.set (get_param_pos (get_ccode_destroy_notify_pos (m)), ctarget_destroy_notify); 347 } 348 } 349 } 350 351 i++; 352 } 353 if (get_ccode_array_length (m) && m.return_type is ArrayType) { 354 var array_type = (ArrayType) m.return_type; 355 for (int dim = 1; dim <= array_type.rank; dim++) { 356 CCodeExpression clength; 357 if (!get_ccode_array_length (d)) { 358 clength = new CCodeConstant ("NULL"); 359 } else { 360 clength = new CCodeIdentifier (get_array_length_cname ("result", dim)); 361 } 362 carg_map.set (get_param_pos (get_ccode_array_length_pos (m) + 0.01 * dim), clength); 363 } 364 } else if (get_ccode_delegate_target (m) && m.return_type is DelegateType) { 365 var deleg_type = (DelegateType) m.return_type; 366 367 if (deleg_type.delegate_symbol.has_target) { 368 var ctarget = new CCodeIdentifier (get_delegate_target_cname ("result")); 369 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (m)), ctarget); 370 if (deleg_type.is_disposable ()) { 371 var ctarget_destroy_notify = new CCodeIdentifier (get_delegate_target_destroy_notify_cname ("result")); 372 carg_map.set (get_param_pos (get_ccode_destroy_notify_pos (m)), ctarget_destroy_notify); 373 } 374 } 375 } else if (m.return_type.is_real_non_null_struct_type ()) { 376 carg_map.set (get_param_pos (-3), new CCodeIdentifier ("result")); 377 } 378 379 if (m.tree_can_fail) { 380 carg_map.set (get_param_pos (get_ccode_error_pos (m)), new CCodeIdentifier ("error")); 381 } 382 383 var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_name (m))); 384 385 // append C arguments in the right order 386 last_pos = -1; 387 while (true) { 388 min_pos = -1; 389 foreach (int pos in carg_map.get_keys ()) { 390 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { 391 min_pos = pos; 392 } 393 } 394 if (min_pos == -1) { 395 break; 396 } 397 ccall.add_argument (carg_map.get (min_pos)); 398 last_pos = min_pos; 399 } 400 401 if (m.coroutine) { 402 ccall.add_argument (new CCodeConstant ("NULL")); 403 ccall.add_argument (new CCodeConstant ("NULL")); 404 } 405 406 if (m.return_type is VoidType || m.return_type.is_real_non_null_struct_type ()) { 407 ccode.add_expression (ccall); 408 if (!(d.return_type is VoidType || d.return_type.is_real_non_null_struct_type ())) { 409 // return a default value 410 ccode.add_declaration (get_ccode_name (creturn_type), new CCodeVariableDeclarator ("result", default_value_for_type (d.return_type, true))); 411 } 412 } else { 413 CCodeExpression result = ccall; 414 if (d.return_type is GenericType) { 415 result = convert_to_generic_pointer (result, m.return_type); 416 } 417 ccode.add_declaration (get_ccode_name (creturn_type), new CCodeVariableDeclarator ("result", result)); 418 } 419 420 if (d.has_target /* TODO: && dt.value_owned */ && dt.is_called_once) { 421 // destroy notify "self" after the call 422 CCodeExpression? destroy_notify = null; 423 if (m.closure) { 424 int block_id = get_block_id (current_closure_block); 425 destroy_notify = new CCodeIdentifier ("block%d_data_unref".printf (block_id)); 426 } else if (get_this_type () != null && m.binding != MemberBinding.STATIC && !m.is_async_callback && is_reference_counting (m.this_parameter.variable_type.type_symbol)) { 427 destroy_notify = get_destroy_func_expression (m.this_parameter.variable_type); 428 } 429 430 if (destroy_notify != null) { 431 var unref_call = new CCodeFunctionCall (destroy_notify); 432 unref_call.add_argument (new CCodeIdentifier ("self")); 433 ccode.add_expression (unref_call); 434 } 435 } 436 437 if (!(m.return_type is VoidType || m.return_type.is_real_non_null_struct_type ()) || 438 !(d.return_type is VoidType || d.return_type.is_real_non_null_struct_type ())) { 439 ccode.add_return (new CCodeIdentifier ("result")); 440 } 441 442 pop_function (); 443 444 // append to file 445 cfile.add_function_declaration (function); 446 cfile.add_function (function); 447 448 return wrapper_name; 449 } 450 451 public override CCodeParameter generate_parameter (Parameter param, CCodeFile decl_space, Map<int,CCodeParameter> cparam_map, Map<int,CCodeExpression>? carg_map) { 452 if (!(param.variable_type is DelegateType || param.variable_type is MethodType)) { 453 return base.generate_parameter (param, decl_space, cparam_map, carg_map); 454 } 455 456 var param_type = param.variable_type; 457 if (param_type is DelegateType && ((DelegateType) param_type).delegate_symbol == param.parent_symbol) { 458 // recursive delegate 459 param_type = new DelegateType ((Delegate) context.root.scope.lookup ("GLib").scope.lookup ("Callback")); 460 } 461 462 generate_type_declaration (param_type, decl_space); 463 464 string ctypename = get_ccode_name (param_type); 465 string target_ctypename = get_ccode_name (delegate_target_type); 466 string target_destroy_notify_ctypename = get_ccode_name (delegate_target_destroy_type); 467 468 if (param.direction != ParameterDirection.IN) { 469 ctypename += "*"; 470 target_ctypename += "*"; 471 target_destroy_notify_ctypename += "*"; 472 } 473 474 var main_cparam = new CCodeParameter (get_ccode_name (param), ctypename); 475 476 cparam_map.set (get_param_pos (get_ccode_pos (param)), main_cparam); 477 if (carg_map != null) { 478 carg_map.set (get_param_pos (get_ccode_pos (param)), get_parameter_cexpression (param)); 479 } 480 481 if (param_type is DelegateType) { 482 unowned DelegateType deleg_type = (DelegateType) param_type; 483 if (get_ccode_delegate_target (param) && deleg_type.delegate_symbol.has_target) { 484 var cparam = new CCodeParameter (get_ccode_delegate_target_name (param), target_ctypename); 485 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), cparam); 486 if (carg_map != null) { 487 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), get_cexpression (cparam.name)); 488 } 489 if (deleg_type.is_disposable ()) { 490 cparam = new CCodeParameter (get_ccode_delegate_target_destroy_notify_name (param), target_destroy_notify_ctypename); 491 cparam_map.set (get_param_pos (get_ccode_destroy_notify_pos (param)), cparam); 492 if (carg_map != null) { 493 carg_map.set (get_param_pos (get_ccode_destroy_notify_pos (param)), get_cexpression (cparam.name)); 494 } 495 } 496 } 497 } else if (param_type is MethodType) { 498 var cparam = new CCodeParameter (get_ccode_delegate_target_name (param), target_ctypename); 499 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), cparam); 500 if (carg_map != null) { 501 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), get_cexpression (cparam.name)); 502 } 503 } 504 505 return main_cparam; 506 } 507} 508