1/* valapropertyaccessor.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 * Represents a get or set accessor of a property in the source code. 27 */ 28public class Vala.PropertyAccessor : Subroutine { 29 /** 30 * The corresponding property. 31 */ 32 public Property prop { 33 get { return parent_symbol as Property; } 34 } 35 36 /** 37 * The property type. 38 */ 39 public DataType? value_type { 40 get { return _value_type; } 41 private set { 42 _value_type = value; 43 if (value != null) { 44 _value_type.parent_node = this; 45 } 46 } 47 } 48 49 /** 50 * Specifies whether this accessor may be used to get the property. 51 */ 52 public bool readable { get; private set; } 53 54 /** 55 * Specifies whether this accessor may be used to set the property. 56 */ 57 public bool writable { get; private set; } 58 59 /** 60 * Specifies whether this accessor may be used to construct the 61 * property. 62 */ 63 public bool construction { get; private set; } 64 65 /** 66 * True if the body was automatically generated 67 */ 68 public bool automatic_body { get; private set; } 69 70 public override bool has_result { 71 get { return readable; } 72 } 73 74 /** 75 * Represents the generated value parameter in a set accessor. 76 */ 77 public Parameter value_parameter { get; private set; } 78 79 private DataType _value_type; 80 81 /** 82 * Creates a new property accessor. 83 * 84 * @param readable true if get accessor, false otherwise 85 * @param writable true if set accessor, false otherwise 86 * @param construction true if construct accessor, false otherwise 87 * @param body accessor body 88 * @param source_reference reference to source code 89 * @return newly created property accessor 90 */ 91 public PropertyAccessor (bool readable, bool writable, bool construction, DataType? value_type, Block? body, SourceReference? source_reference = null, Comment? comment = null) { 92 base (null, source_reference, comment); 93 this.readable = readable; 94 this.writable = writable; 95 this.construction = construction; 96 this.value_type = value_type; 97 this.body = body; 98 this.access = SymbolAccessibility.PUBLIC; 99 } 100 101 public override void accept (CodeVisitor visitor) { 102 visitor.visit_property_accessor (this); 103 } 104 105 public override void accept_children (CodeVisitor visitor) { 106 value_type.accept (visitor); 107 108 if (result_var != null) { 109 result_var.accept (visitor); 110 } 111 112 if (body != null) { 113 body.accept (visitor); 114 } 115 } 116 117 /** 118 * Get the method representing this property accessor 119 * @return null if the accessor is neither readable nor writable 120 */ 121 public Method? get_method () { 122 Method? m = null; 123 if (readable) { 124 m = new Method ("get_%s".printf (prop.name), value_type, source_reference, comment); 125 } else if (writable) { 126 m = new Method ("set_%s".printf (prop.name), new VoidType(), source_reference, comment); 127 m.add_parameter (value_parameter.copy ()); 128 } 129 130 if (m != null) { 131 m.owner = prop.owner; 132 m.access = access; 133 m.binding = prop.binding; 134 m.is_abstract = prop.is_abstract; 135 m.is_virtual = prop.is_virtual; 136 m.this_parameter = prop.this_parameter; 137 138 // Inherit important attributes 139 m.copy_attribute_bool (prop, "GIR", "visible"); 140 } 141 142 return m; 143 } 144 145 public override bool check (CodeContext context) { 146 if (checked) { 147 return !error; 148 } 149 150 checked = true; 151 152 if (!value_type.check (context)) { 153 error = true; 154 return false; 155 } 156 157 var old_symbol = context.analyzer.current_symbol; 158 159 context.analyzer.current_symbol = this; 160 161 if (writable || construction) { 162 value_parameter = new Parameter ("value", value_type, source_reference); 163 // Inherit important attributes 164 value_parameter.copy_attribute_bool (prop, "CCode", "array_length"); 165 value_parameter.copy_attribute_bool (prop, "CCode", "array_null_terminated"); 166 value_parameter.copy_attribute_bool (prop, "CCode", "delegate_target"); 167 } 168 169 if (context.profile == Profile.GOBJECT 170 && readable && ((TypeSymbol) prop.parent_symbol).is_subtype_of (context.analyzer.object_type)) { 171 //FIXME Code duplication with CCodeMemberAccessModule.visit_member_access() 172 if (prop.get_attribute ("NoAccessorMethod") != null) { 173 if (value_type.is_real_struct_type ()) { 174 if (source_reference == null || source_reference.file == null) { 175 // Hopefully good as is 176 } else if (!value_type.value_owned && source_reference.file.file_type == SourceFileType.SOURCE) { 177 error = true; 178 Report.error (source_reference, "unowned return value for getter of property `%s' not supported without accessor".printf (prop.get_full_name ())); 179 } 180 } else if (value_type.value_owned && (source_reference == null || source_reference.file == null)) { 181 if (value_type is DelegateType || value_type is PointerType || (value_type is ValueType && !value_type.nullable)) { 182 value_type.value_owned = false; 183 } 184 } 185 } 186 } 187 188 if (prop.source_type == SourceFileType.SOURCE) { 189 if (body == null && !prop.interface_only && !prop.is_abstract) { 190 /* no accessor body specified, insert default body */ 191 192 automatic_body = true; 193 body = new Block (source_reference); 194 var ma = new MemberAccess.simple ("_%s".printf (prop.name), source_reference); 195 if (readable) { 196 body.add_statement (new ReturnStatement (ma, source_reference)); 197 } else { 198 Expression value = new MemberAccess.simple ("value", source_reference); 199 if (value_type.value_owned) { 200 value = new ReferenceTransferExpression (value, source_reference); 201 } 202 var assignment = new Assignment (ma, value, AssignmentOperator.SIMPLE, source_reference); 203 body.add_statement (new ExpressionStatement (assignment)); 204 } 205 } 206 } 207 208 if ((prop.is_abstract || prop.is_virtual || prop.overrides) && access == SymbolAccessibility.PRIVATE) { 209 error = true; 210 Report.error (source_reference, "Property `%s' with private accessor cannot be marked as abstract, virtual or override".printf (prop.get_full_name ())); 211 return false; 212 } 213 214 if (context.profile == Profile.POSIX && construction) { 215 error = true; 216 Report.error (source_reference, "`construct' is not supported in POSIX profile"); 217 return false; 218 } else if (construction && !((TypeSymbol) prop.parent_symbol).is_subtype_of (context.analyzer.object_type)) { 219 error = true; 220 Report.error (source_reference, "construct properties require `GLib.Object'"); 221 return false; 222 } else if (construction && !context.analyzer.is_gobject_property (prop)) { 223 //TODO Report an error for external property too 224 if (external_package) { 225 Report.warning (source_reference, "construct properties not supported for specified property type"); 226 } else { 227 error = true; 228 Report.error (source_reference, "construct properties not supported for specified property type"); 229 return false; 230 } 231 } 232 233 if (body != null && prop.is_abstract) { 234 error = true; 235 Report.error (source_reference, "Accessor of abstract property `%s' cannot have body".printf (prop.get_full_name ())); 236 return false; 237 } 238 239 if (body != null) { 240 if (writable || construction) { 241 body.scope.add (value_parameter.name, value_parameter); 242 } 243 244 body.check (context); 245 } 246 247 if (body != null && !body.error) { 248 var error_types = new ArrayList<DataType> (); 249 body.get_error_types (error_types); 250 foreach (DataType body_error_type in error_types) { 251 if (!((ErrorType) body_error_type).dynamic_error) { 252 Report.warning (body_error_type.source_reference, "unhandled error `%s'".printf (body_error_type.to_string())); 253 } 254 } 255 } 256 257 context.analyzer.current_symbol = old_symbol; 258 259 return !error; 260 } 261 262 public override void replace_type (DataType old_type, DataType new_type) { 263 if (value_type == old_type) { 264 value_type = new_type; 265 } 266 } 267} 268