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