1 /* valalambdaexpression.vala
3 * Copyright (C) 2006-2010 Jürg Billeter
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.
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.
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
20 * Jürg Billeter <j@bitron.ch>
26 * Represents a lambda expression in the source code. Lambda expressions are
27 * anonymous methods with implicitly typed parameters.
29 public class Vala
.LambdaExpression
: Expression
{
31 * The expression body of this lambda expression. Only one of
32 * expression_body or statement_body may be set.
34 public Expression expression_body
{ get; set; }
37 * The statement body of this lambda expression. Only one of
38 * expression_body or statement_body may be set.
40 public Block statement_body
{ get; set; }
43 * The generated method.
45 public Method method
{ get; set; }
47 private List
<string> parameters
= new ArrayList
<string> ();
50 * Creates a new lambda expression.
52 * @param expression_body expression body
53 * @param source_reference reference to source code
54 * @return newly created lambda expression
56 public LambdaExpression (Expression expression_body
, SourceReference source_reference
) {
57 this
.source_reference
= source_reference
;
58 this
.expression_body
= expression_body
;
62 * Creates a new lambda expression with statement body.
64 * @param statement_body statement body
65 * @param source_reference reference to source code
66 * @return newly created lambda expression
68 public LambdaExpression
.with_statement_body (Block statement_body
, SourceReference source_reference
) {
69 this
.statement_body
= statement_body
;
70 this
.source_reference
= source_reference
;
74 * Appends implicitly typed parameter.
76 * @param param parameter name
78 public void add_parameter (string param
) {
79 parameters
.add (param
);
83 * Returns copy of parameter list.
85 * @return parameter list
87 public List
<string> get_parameters () {
91 public override void accept (CodeVisitor visitor
) {
92 visitor
.visit_lambda_expression (this
);
94 visitor
.visit_expression (this
);
97 public override void accept_children (CodeVisitor visitor
) {
99 if (expression_body
!= null) {
100 expression_body
.accept (visitor
);
101 visitor
.visit_end_full_expression (expression_body
);
102 } else if (statement_body
!= null) {
103 statement_body
.accept (visitor
);
106 method
.accept (visitor
);
110 public override bool is_pure () {
114 string get_lambda_name (CodeContext context
) {
115 var result
= "_lambda%d_".printf (context
.analyzer
.next_lambda_id
);
117 context
.analyzer
.next_lambda_id
++;
122 public override bool check (CodeContext context
) {
129 if (!(target_type is DelegateType
)) {
131 Report
.error (source_reference
, "lambda expression not allowed in this context");
135 var cb
= (Delegate
) ((DelegateType
) target_type
).delegate_symbol
;
136 var return_type
= cb
.return_type
.get_actual_type (target_type
, null, this
);
137 method
= new
Method (get_lambda_name (context
), return_type
, source_reference
);
138 // track usage for flow analyzer
140 method
.check_deprecated (source_reference
);
142 if (!cb
.has_target
|| !context
.analyzer
.is_in_instance_method ()) {
143 method
.binding
= MemberBinding
.STATIC
;
145 var sym
= context
.analyzer
.current_symbol
;
146 while (method
.this_parameter
== null) {
147 if (sym is Property
) {
148 var prop
= (Property
) sym
;
149 method
.this_parameter
= prop
.this_parameter
;
150 } else if (sym is Constructor
) {
151 var c
= (Constructor
) sym
;
152 method
.this_parameter
= c
.this_parameter
;
153 } else if (sym is Destructor
) {
154 var d
= (Destructor
) sym
;
155 method
.this_parameter
= d
.this_parameter
;
156 } else if (sym is Method
) {
157 var m
= (Method
) sym
;
158 method
.this_parameter
= m
.this_parameter
;
161 sym
= sym
.parent_symbol
;
164 method
.owner
= context
.analyzer
.current_symbol
.scope
;
166 if (!(method
.return_type is VoidType
) && CodeContext
.get ().profile
== Profile
.DOVA
) {
167 method
.result_var
= new
LocalVariable (method
.return_type
.copy (), "result", null, source_reference
);
168 method
.result_var
.is_result
= true;
171 var lambda_params
= get_parameters ();
172 Iterator
<string> lambda_param_it
= lambda_params
.iterator ();
174 if (cb
.sender_type
!= null && lambda_params
.size
== cb
.get_parameters ().size
+ 1) {
175 // lambda expression has sender parameter
176 lambda_param_it
.next ();
178 string lambda_param
= lambda_param_it
.get ();
179 var param
= new
Parameter (lambda_param
, cb
.sender_type
);
180 method
.add_parameter (param
);
183 foreach (Parameter cb_param
in cb
.get_parameters ()) {
184 if (!lambda_param_it
.next ()) {
185 /* lambda expressions are allowed to have less parameters */
189 string lambda_param
= lambda_param_it
.get ();
190 var param_type
= cb_param
.variable_type
.get_actual_type (target_type
, null, this
);
191 var param
= new
Parameter (lambda_param
, param_type
);
192 method
.add_parameter (param
);
195 if (lambda_param_it
.next ()) {
196 /* lambda expressions may not expect more parameters */
198 Report
.error (source_reference
, "lambda expression: too many parameters");
202 foreach (var error_type
in cb
.get_error_types ()) {
203 method
.add_error_type (error_type
.copy ());
206 if (expression_body
!= null) {
207 var block
= new
Block (source_reference
);
208 block
.scope
.parent_scope
= method
.scope
;
210 if (method
.return_type
.data_type
!= null) {
211 if (context
.profile
== Profile
.DOVA
) {
212 block
.add_statement (new
ExpressionStatement (new
Assignment (new MemberAccess
.simple ("result", source_reference
), expression_body
, AssignmentOperator
.SIMPLE
, source_reference
), source_reference
));
213 block
.add_statement (new
ReturnStatement (null, source_reference
));
215 block
.add_statement (new
ReturnStatement (expression_body
, source_reference
));
218 block
.add_statement (new
ExpressionStatement (expression_body
, source_reference
));
223 method
.body
= statement_body
;
225 method
.body
.owner
= method
.scope
;
227 // support use of generics in closures
228 var m
= context
.analyzer
.find_parent_method (context
.analyzer
.current_symbol
);
230 foreach (var type_param
in m
.get_type_parameters ()) {
231 method
.add_type_parameter (new
TypeParameter (type_param
.name
, type_param
.source_reference
));
233 method
.closure
= true;
234 m
.body
.captured
= true;
238 /* lambda expressions should be usable like MemberAccess of a method */
239 symbol_reference
= method
;
241 method
.check (context
);
243 value_type
= new
MethodType (method
);
244 value_type
.value_owned
= target_type
.value_owned
;
249 public override void emit (CodeGenerator codegen
) {
250 codegen
.visit_lambda_expression (this
);
252 codegen
.visit_expression (this
);
255 public override void get_used_variables (Collection
<LocalVariable
> collection
) {
256 // require captured variables to be initialized
257 if (method
.closure
) {
258 method
.get_captured_variables (collection
);