Line data Source code
1 : /* valalambdaexpression.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 :
23 : using GLib;
24 :
25 : /**
26 : * Represents a lambda expression in the source code.
27 : *
28 : * Lambda expressions are anonymous methods with implicitly typed parameters.
29 : */
30 14493 : public class Vala.LambdaExpression : Expression {
31 : /**
32 : * The expression body of this lambda expression. Only one of
33 : * expression_body or statement_body may be set.
34 : */
35 : public Expression expression_body {
36 32547 : get { return _expression_body; }
37 1552 : private set {
38 36343 : _expression_body = value;
39 1552 : if (_expression_body != null) {
40 1552 : _expression_body.parent_node = this;
41 : }
42 : }
43 : }
44 :
45 : /**
46 : * The statement body of this lambda expression. Only one of
47 : * expression_body or statement_body may be set.
48 : */
49 : public Block statement_body {
50 29020 : get { return _statement_body; }
51 4930 : private set {
52 9860 : _statement_body = value;
53 4930 : if (_statement_body != null) {
54 4930 : _statement_body.parent_node = this;
55 : }
56 : }
57 : }
58 :
59 : /**
60 : * The generated method.
61 : */
62 35140 : public Method method { get; private set; }
63 :
64 12964 : private List<Parameter> parameters = new ArrayList<Parameter> ();
65 6482 : Block _statement_body;
66 6482 : Expression _expression_body;
67 :
68 : /**
69 : * Creates a new lambda expression.
70 : *
71 : * @param expression_body expression body
72 : * @param source_reference reference to source code
73 : * @return newly created lambda expression
74 : */
75 4656 : public LambdaExpression (Expression expression_body, SourceReference? source_reference = null) {
76 1552 : this.source_reference = source_reference;
77 1552 : this.expression_body = expression_body;
78 : }
79 :
80 : /**
81 : * Creates a new lambda expression with statement body.
82 : *
83 : * @param statement_body statement body
84 : * @param source_reference reference to source code
85 : * @return newly created lambda expression
86 : */
87 14790 : public LambdaExpression.with_statement_body (Block statement_body, SourceReference? source_reference = null) {
88 4930 : this.statement_body = statement_body;
89 4930 : this.source_reference = source_reference;
90 : }
91 :
92 : /**
93 : * Appends implicitly typed parameter.
94 : *
95 : * @param param parameter name
96 : */
97 12410 : public void add_parameter (Parameter param) {
98 12410 : parameters.add (param);
99 : }
100 :
101 : /**
102 : * Returns the parameter list.
103 : *
104 : * @return parameter list
105 : */
106 6020 : public unowned List<Parameter> get_parameters () {
107 6020 : return parameters;
108 : }
109 :
110 21018 : public override void accept (CodeVisitor visitor) {
111 21018 : visitor.visit_lambda_expression (this);
112 :
113 21018 : visitor.visit_expression (this);
114 : }
115 :
116 21371 : public override void accept_children (CodeVisitor visitor) {
117 21371 : if (method == null) {
118 16509 : if (expression_body != null) {
119 4290 : expression_body.accept (visitor);
120 4290 : visitor.visit_end_full_expression (expression_body);
121 12219 : } else if (statement_body != null) {
122 12219 : statement_body.accept (visitor);
123 : }
124 : } else {
125 4862 : method.accept (visitor);
126 : }
127 : }
128 :
129 0 : public override bool is_pure () {
130 0 : return false;
131 : }
132 :
133 6021 : public override bool check (CodeContext context) {
134 6021 : if (checked) {
135 0 : return !error;
136 : }
137 :
138 6021 : checked = true;
139 :
140 6021 : if (!(target_type is DelegateType)) {
141 1 : error = true;
142 1 : if (target_type != null) {
143 1 : Report.error (source_reference, "Cannot convert lambda expression to `%s'", target_type.to_string ());
144 : } else {
145 0 : Report.error (source_reference, "lambda expression not allowed in this context");
146 : }
147 1 : return false;
148 : }
149 :
150 6020 : var cb = (Delegate) ((DelegateType) target_type).delegate_symbol;
151 6020 : var return_type = cb.return_type.get_actual_type (target_type, null, this);
152 6020 : method = new Method ("@lambda", return_type, source_reference);
153 : // track usage for flow analyzer
154 6020 : method.used = true;
155 :
156 6020 : if (return_type is ArrayType) {
157 2 : method.copy_attribute_bool (cb, "CCode", "array_length");
158 2 : method.copy_attribute_bool (cb, "CCode", "array_null_terminated");
159 2 : method.copy_attribute_string (cb, "CCode", "array_length_type");
160 6018 : } else if (return_type is DelegateType) {
161 1 : method.copy_attribute_bool (cb, "CCode", "delegate_target");
162 : }
163 :
164 10474 : if (!cb.has_target || !context.analyzer.is_in_instance_method ()) {
165 1566 : method.binding = MemberBinding.STATIC;
166 : } else {
167 4454 : var sym = context.analyzer.current_symbol;
168 13367 : while (method.this_parameter == null) {
169 8914 : if (sym is Property) {
170 1 : var prop = (Property) sym;
171 1 : method.this_parameter = prop.this_parameter;
172 8913 : } else if (sym is Constructor) {
173 1 : var c = (Constructor) sym;
174 1 : method.this_parameter = c.this_parameter;
175 8912 : } else if (sym is Destructor) {
176 1 : var d = (Destructor) sym;
177 1 : method.this_parameter = d.this_parameter;
178 13361 : } else if (sym is Method) {
179 4451 : var m = (Method) sym;
180 4451 : method.this_parameter = m.this_parameter;
181 : }
182 :
183 17826 : sym = sym.parent_symbol;
184 : }
185 : }
186 6020 : method.owner = context.analyzer.current_symbol.scope;
187 6020 : method.parent_node = this;
188 :
189 6020 : var lambda_params = get_parameters ();
190 6020 : Iterator<Parameter> lambda_param_it = lambda_params.iterator ();
191 :
192 6025 : if (cb.sender_type != null && lambda_params.size == cb.get_parameters ().size + 1) {
193 : // lambda expression has sender parameter
194 5 : lambda_param_it.next ();
195 :
196 5 : Parameter lambda_param = lambda_param_it.get ();
197 5 : lambda_param.variable_type = cb.sender_type;
198 5 : method.add_parameter (lambda_param);
199 : }
200 :
201 29000 : foreach (Parameter cb_param in cb.get_parameters ()) {
202 11509 : if (!lambda_param_it.next ()) {
203 : /* lambda expressions are allowed to have less parameters */
204 19 : break;
205 : }
206 :
207 11490 : Parameter lambda_param = lambda_param_it.get ();
208 :
209 11490 : if (lambda_param.direction != cb_param.direction) {
210 0 : error = true;
211 0 : Report.error (lambda_param.source_reference, "direction of parameter `%s' is incompatible with the target delegate", lambda_param.name);
212 : }
213 :
214 11490 : lambda_param.variable_type = cb_param.variable_type.get_actual_type (target_type, null, this);
215 11490 : lambda_param.base_parameter = cb_param;
216 11490 : method.add_parameter (lambda_param);
217 : }
218 :
219 6020 : if (lambda_param_it.next ()) {
220 : /* lambda expressions may not expect more parameters */
221 0 : error = true;
222 0 : Report.error (source_reference, "lambda expression: too many parameters");
223 0 : return false;
224 : }
225 :
226 6020 : var error_types = new ArrayList<DataType> ();
227 6020 : cb.get_error_types (error_types);
228 6288 : foreach (var error_type in error_types) {
229 134 : method.add_error_type (error_type.copy ());
230 : }
231 :
232 7458 : if (expression_body != null) {
233 1438 : var block = new Block (source_reference);
234 1438 : block.scope.parent_scope = method.scope;
235 :
236 1438 : if (method.return_type.type_symbol != null) {
237 17 : block.add_statement (new ReturnStatement (expression_body, source_reference));
238 : } else {
239 1421 : block.add_statement (new ExpressionStatement (expression_body, source_reference));
240 : }
241 :
242 1438 : method.body = block;
243 : } else {
244 4582 : method.body = statement_body;
245 : }
246 6020 : method.body.owner = method.scope;
247 :
248 : // support use of generics in closures
249 6020 : unowned Method? m = SemanticAnalyzer.find_parent_method (context.analyzer.current_symbol);
250 6020 : if (m != null) {
251 8853 : foreach (var type_param in m.get_type_parameters ()) {
252 1418 : method.add_type_parameter (new TypeParameter (type_param.name, type_param.source_reference));
253 :
254 1418 : method.closure = true;
255 1418 : m.body.captured = true;
256 : }
257 : }
258 :
259 : /* lambda expressions should be usable like MemberAccess of a method */
260 6020 : symbol_reference = method;
261 :
262 6020 : method.check (context);
263 :
264 6020 : value_type = new MethodType (method, source_reference);
265 6020 : value_type.value_owned = target_type.value_owned;
266 :
267 6020 : return !error;
268 : }
269 :
270 353 : public override void emit (CodeGenerator codegen) {
271 353 : codegen.visit_lambda_expression (this);
272 :
273 353 : codegen.visit_expression (this);
274 : }
275 :
276 4202 : public override void get_used_variables (Collection<Variable> collection) {
277 : // require captured variables to be initialized
278 4202 : if (method != null && method.closure) {
279 3944 : method.get_captured_variables ((Collection<LocalVariable>) collection);
280 : }
281 : }
282 : }
|