LCOV - code coverage report
Current view: top level - vala - valaforeachstatement.vala (source / functions) Coverage Total Hit
Test: vala 0.57.0.298-a8cae1 Lines: 94.6 % 258 244
Test Date: 2024-04-25 11:34:36 Functions: - 0 0

            Line data    Source code
       1              : /* valaforeachstatement.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              : 
      24              : /**
      25              :  * Represents a foreach statement in the source code.
      26              :  *
      27              :  * Foreach statements iterate over the elements of a collection.
      28              :  */
      29          702 : public class Vala.ForeachStatement : Block {
      30              :         /**
      31              :          * Specifies the element type.
      32              :          */
      33              :         public DataType? type_reference {
      34         3967 :                 get { return _data_type; }
      35         1196 :                 private set {
      36         3128 :                         _data_type = value;
      37         1196 :                         if (_data_type != null) {
      38         1196 :                                 _data_type.parent_node = this;
      39              :                         }
      40              :                 }
      41              :         }
      42              : 
      43              :         /**
      44              :          * Specifies the element variable name.
      45              :          */
      46         2126 :         public string variable_name { get; private set; }
      47              : 
      48              :         /**
      49              :          * Specifies the container.
      50              :          */
      51              :         public Expression collection {
      52         7012 :                 get {
      53         7012 :                         return _collection;
      54              :                 }
      55          656 :                 private set {
      56         1312 :                         _collection = value;
      57          656 :                         _collection.parent_node = this;
      58              :                 }
      59              :         }
      60              : 
      61              :         /**
      62              :          * Specifies the loop body.
      63              :          */
      64              :         public Block body {
      65         3236 :                 get {
      66         3236 :                         return _body;
      67              :                 }
      68          653 :                 private set {
      69         1306 :                         _body = value;
      70          653 :                         _body.parent_node = this;
      71              :                 }
      72              :         }
      73              : 
      74          483 :         public bool use_iterator { get; private set; }
      75              : 
      76              :         /**
      77              :          * Specifies the declarator for the generated element variable.
      78              :          */
      79         1334 :         public LocalVariable element_variable { get; private set; }
      80              : 
      81              :         /**
      82              :          * Specifies the declarator for the generated collection variable.
      83              :          */
      84         1154 :         public LocalVariable collection_variable { get; private set; }
      85              : 
      86              :         /**
      87              :          * Specifies the declarator for the generated iterator variable.
      88              :          */
      89          653 :         public LocalVariable iterator_variable { get; private set; }
      90              : 
      91          653 :         private Expression _collection;
      92          653 :         private Block _body;
      93              : 
      94          653 :         private DataType _data_type;
      95              : 
      96              :         /**
      97              :          * Creates a new foreach statement.
      98              :          *
      99              :          * @param type_reference    element type
     100              :          * @param variable_name     element variable name
     101              :          * @param collection        container
     102              :          * @param body              loop body
     103              :          * @param source_reference  reference to source code
     104              :          * @return                  newly created foreach statement
     105              :          */
     106         1959 :         public ForeachStatement (DataType? type_reference, string variable_name, Expression collection, Block body, SourceReference? source_reference = null) {
     107          653 :                 base (source_reference);
     108          653 :                 this.variable_name = variable_name;
     109          653 :                 this.collection = collection;
     110          653 :                 this.body = body;
     111          653 :                 this.type_reference = type_reference;
     112              :         }
     113              : 
     114         1288 :         public override void accept (CodeVisitor visitor) {
     115         1288 :                 if (use_iterator) {
     116          468 :                         base.accept (visitor);
     117          468 :                         return;
     118              :                 }
     119              : 
     120          820 :                 visitor.visit_foreach_statement (this);
     121              :         }
     122              : 
     123         1121 :         public override void accept_children (CodeVisitor visitor) {
     124         1121 :                 if (use_iterator) {
     125          468 :                         base.accept_children (visitor);
     126          468 :                         return;
     127              :                 }
     128              : 
     129          653 :                 collection.accept (visitor);
     130          653 :                 visitor.visit_end_full_expression (collection);
     131              : 
     132          653 :                 if (type_reference != null) {
     133          653 :                         type_reference.accept (visitor);
     134              :                 }
     135              : 
     136          653 :                 body.accept (visitor);
     137              :         }
     138              : 
     139            3 :         public override void replace_expression (Expression old_node, Expression new_node) {
     140            3 :                 if (collection == old_node) {
     141            3 :                         collection = new_node;
     142              :                 }
     143              :         }
     144              : 
     145          496 :         public override void replace_type (DataType old_type, DataType new_type) {
     146          496 :                 if (type_reference == old_type) {
     147          496 :                         type_reference = new_type;
     148              :                 }
     149              :         }
     150              : 
     151          653 :         public override bool check (CodeContext context) {
     152          653 :                 if (checked) {
     153            0 :                         return !error;
     154              :                 }
     155              : 
     156          653 :                 checked = true;
     157              : 
     158          653 :                 if (type_reference == null) {
     159            0 :                         type_reference = new VarType ();
     160              :                 }
     161              : 
     162              :                 // analyze collection expression first, used for type inference
     163          653 :                 if (!collection.check (context)) {
     164              :                         // ignore inner error
     165            0 :                         error = true;
     166            0 :                         return false;
     167          653 :                 } else if (collection.value_type == null) {
     168            0 :                         Report.error (collection.source_reference, "invalid collection expression");
     169            0 :                         error = true;
     170            0 :                         return false;
     171              :                 }
     172              : 
     173          653 :                 var collection_type = collection.value_type.copy ();
     174          653 :                 collection.target_type = collection_type.copy ();
     175              : 
     176          653 :                 if (collection_type is ArrayType) {
     177          122 :                         var array_type = (ArrayType) collection_type;
     178              : 
     179              :                         // can't use inline-allocated array for temporary variable
     180          122 :                         array_type.inline_allocated = false;
     181              : 
     182          122 :                         return check_without_iterator (context, collection_type, array_type.element_type);
     183         2509 :                 } else if (context.profile == Profile.GOBJECT && (collection_type.compatible (context.analyzer.glist_type)
     184          500 :                     || collection_type.compatible (context.analyzer.gslist_type)
     185          499 :                     || collection_type.compatible (context.analyzer.genericarray_type)
     186          492 :                     || collection_type.compatible (context.analyzer.garray_type)
     187          487 :                     || collection_type.compatible (context.analyzer.gsequence_type))) {
     188           47 :                         if (collection_type.get_type_arguments ().size != 1) {
     189            1 :                                 error = true;
     190            1 :                                 Report.error (collection.source_reference, "missing type argument for collection");
     191            1 :                                 return false;
     192              :                         }
     193              : 
     194           46 :                         return check_without_iterator (context, collection_type, collection_type.get_type_arguments ().get (0));
     195          484 :                 } else if (context.profile == Profile.GOBJECT && collection_type.compatible (context.analyzer.gvaluearray_type)) {
     196            2 :                         return check_without_iterator (context, collection_type, context.analyzer.gvalue_type);
     197          482 :                 } else if (context.profile == Profile.GOBJECT && collection_type.compatible (context.analyzer.string_type)) {
     198            1 :                         return check_without_iterator (context, collection_type, context.analyzer.unichar_type);
     199              :                 } else {
     200          481 :                         return check_with_iterator (context, collection_type);
     201              :                 }
     202              :         }
     203              : 
     204          481 :         bool check_with_index (CodeContext context, DataType collection_type) {
     205          481 :                 var get_method = collection_type.get_member ("get") as Method;
     206          481 :                 if (get_method == null) {
     207           60 :                         return false;
     208              :                 }
     209          421 :                 unowned List<Parameter> parameters = get_method.get_parameters ();
     210          421 :                 if (parameters.size != 1 || !(parameters[0].variable_type is IntegerType)) {
     211            2 :                         return false;
     212              :                 }
     213          419 :                 var size_property = collection_type.get_member ("size") as Property;
     214          419 :                 if (size_property == null) {
     215            0 :                         return false;
     216              :                 }
     217              : 
     218          419 :                 var list_type = collection_type.copy ();
     219          419 :                 if (collection.symbol_reference is Variable) {
     220          160 :                         list_type.value_owned = false;
     221              :                 }
     222          419 :                 add_statement (new DeclarationStatement (new LocalVariable (list_type, "_%s_list".printf (variable_name), collection, source_reference), source_reference));
     223          419 :                 add_statement (new DeclarationStatement (new LocalVariable (null, "_%s_size".printf (variable_name), new MemberAccess (new MemberAccess.simple ("_%s_list".printf (variable_name), source_reference), "size", source_reference), source_reference), source_reference));
     224          419 :                 add_statement (new DeclarationStatement (new LocalVariable (null, "_%s_index".printf (variable_name), new UnaryExpression (UnaryOperator.MINUS, new IntegerLiteral ("1", source_reference), source_reference), source_reference), source_reference));
     225          419 :                 var next = new UnaryExpression (UnaryOperator.INCREMENT, new MemberAccess.simple ("_%s_index".printf (variable_name), source_reference), source_reference);
     226          419 :                 var conditional = new BinaryExpression (BinaryOperator.LESS_THAN, next, new MemberAccess.simple ("_%s_size".printf (variable_name), source_reference), source_reference);
     227          419 :                 var loop = new WhileStatement (conditional, body, source_reference);
     228          419 :                 add_statement (loop);
     229              : 
     230          419 :                 var get_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_list".printf (variable_name), source_reference), "get", source_reference), source_reference);
     231          419 :                 get_call.add_argument (new MemberAccess.simple ("_%s_index".printf (variable_name), source_reference));
     232          419 :                 body.insert_statement (0, new DeclarationStatement (new LocalVariable (type_reference, variable_name, get_call, source_reference), source_reference));
     233              : 
     234          419 :                 checked = false;
     235          419 :                 return base.check (context);
     236              :         }
     237              : 
     238          481 :         bool check_with_iterator (CodeContext context, DataType collection_type) {
     239          481 :                 use_iterator = true;
     240              : 
     241          481 :                 if (check_with_index (context, collection_type)) {
     242          481 :                         return true;
     243              :                 }
     244              : 
     245           62 :                 var iterator_method = collection_type.get_member ("iterator") as Method;
     246            1 :                 if (iterator_method == null) {
     247            1 :                         Report.error (collection.source_reference, "`%s' does not have an `iterator' method", collection_type.to_string ());
     248            1 :                         error = true;
     249            1 :                         return false;
     250              :                 }
     251           61 :                 if (iterator_method.get_parameters ().size != 0) {
     252            1 :                         Report.error (collection.source_reference, "`%s' must not have any parameters", iterator_method.get_full_name ());
     253            1 :                         error = true;
     254            1 :                         return false;
     255              :                 }
     256           60 :                 var iterator_type = iterator_method.return_type.get_actual_type (collection_type, null, this);
     257           60 :                 if (iterator_type is VoidType) {
     258            1 :                         Report.error (collection.source_reference, "`%s' must return an iterator", iterator_method.get_full_name ());
     259            1 :                         error = true;
     260            1 :                         return false;
     261              :                 }
     262              : 
     263           59 :                 var iterator_call = new MethodCall (new MemberAccess (collection, "iterator", source_reference), source_reference);
     264           59 :                 add_statement (new DeclarationStatement (new LocalVariable (iterator_type, "_%s_it".printf (variable_name), iterator_call, source_reference), source_reference));
     265              : 
     266           59 :                 var next_value_method = iterator_type.get_member ("next_value") as Method;
     267           59 :                 var next_method = iterator_type.get_member ("next") as Method;
     268           61 :                 if (next_value_method != null) {
     269            4 :                         if (next_value_method.get_parameters ().size != 0) {
     270            1 :                                 Report.error (collection.source_reference, "`%s' must not have any parameters", next_value_method.get_full_name ());
     271            1 :                                 error = true;
     272            1 :                                 return false;
     273              :                         }
     274            3 :                         var element_type = next_value_method.return_type.get_actual_type (iterator_type, null, this);
     275            3 :                         if (!element_type.nullable) {
     276            1 :                                 Report.error (collection.source_reference, "return type of `%s' must be nullable", next_value_method.get_full_name ());
     277            1 :                                 error = true;
     278            1 :                                 return false;
     279              :                         }
     280              : 
     281            2 :                         if (!analyze_element_type (element_type)) {
     282            0 :                                 return false;
     283              :                         }
     284              : 
     285            2 :                         add_statement (new DeclarationStatement (new LocalVariable (type_reference, variable_name, null, source_reference), source_reference));
     286              : 
     287            2 :                         var next_value_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "next_value", source_reference), source_reference);
     288            2 :                         var assignment = new Assignment (new MemberAccess (null, variable_name, source_reference), next_value_call, AssignmentOperator.SIMPLE, source_reference);
     289            2 :                         var conditional = new BinaryExpression (BinaryOperator.INEQUALITY, assignment, new NullLiteral (source_reference), source_reference);
     290            2 :                         var loop = new WhileStatement (conditional, body, source_reference);
     291            2 :                         add_statement (loop);
     292          102 :                 } else if (next_method != null) {
     293           54 :                         if (next_method.get_parameters ().size != 0) {
     294            1 :                                 Report.error (collection.source_reference, "`%s' must not have any parameters", next_method.get_full_name ());
     295            1 :                                 error = true;
     296            1 :                                 return false;
     297              :                         }
     298           53 :                         if (!next_method.return_type.compatible (context.analyzer.bool_type)) {
     299            1 :                                 Report.error (collection.source_reference, "`%s' must return a boolean value", next_method.get_full_name ());
     300            1 :                                 error = true;
     301            1 :                                 return false;
     302              :                         }
     303           52 :                         var get_method = iterator_type.get_member ("get") as Method;
     304            1 :                         if (get_method == null) {
     305            1 :                                 Report.error (collection.source_reference, "`%s' does not have a `get' method", iterator_type.to_string ());
     306            1 :                                 error = true;
     307            1 :                                 return false;
     308              :                         }
     309           51 :                         if (get_method.get_parameters ().size != 0) {
     310            1 :                                 Report.error (collection.source_reference, "`%s' must not have any parameters", get_method.get_full_name ());
     311            1 :                                 error = true;
     312            1 :                                 return false;
     313              :                         }
     314           50 :                         var element_type = get_method.return_type.get_actual_type (iterator_type, null, this);
     315           50 :                         if (element_type is VoidType) {
     316            1 :                                 Report.error (collection.source_reference, "`%s' must return an element", get_method.get_full_name ());
     317            1 :                                 error = true;
     318            1 :                                 return false;
     319              :                         }
     320              : 
     321           49 :                         if (!analyze_element_type (element_type)) {
     322            2 :                                 return false;
     323              :                         }
     324              : 
     325           47 :                         var next_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "next", source_reference), source_reference);
     326           47 :                         var loop = new WhileStatement (next_call, body, source_reference);
     327           47 :                         add_statement (loop);
     328              : 
     329           47 :                         var get_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "get", source_reference), source_reference);
     330           47 :                         body.insert_statement (0, new DeclarationStatement (new LocalVariable (type_reference, variable_name, get_call, source_reference), source_reference));
     331              :                 } else {
     332            1 :                         Report.error (collection.source_reference, "`%s' does not have a `next_value' or `next' method", iterator_type.to_string ());
     333            1 :                         error = true;
     334            1 :                         return false;
     335              :                 }
     336              : 
     337           49 :                 checked = false;
     338           49 :                 return base.check (context);
     339              :         }
     340              : 
     341           51 :         bool analyze_element_type (DataType element_type) {
     342              :                 // analyze element type
     343           51 :                 if (type_reference is VarType) {
     344              :                         // var type
     345           14 :                         bool nullable = type_reference.nullable;
     346           14 :                         bool value_owned = type_reference.value_owned;
     347           14 :                         bool is_dynamic = type_reference.is_dynamic;
     348           14 :                         type_reference = element_type.copy ();
     349              :                         // FIXME Only follows "unowned var" otherwise inherit ownership of element-type
     350           14 :                         if (!value_owned) {
     351            0 :                                 type_reference.value_owned = false;
     352              :                         }
     353           14 :                         if (nullable) {
     354            0 :                                 type_reference.nullable = true;
     355              :                         }
     356           14 :                         if (is_dynamic) {
     357            0 :                                 type_reference.is_dynamic = true;
     358              :                         }
     359           37 :                 } else if (!element_type.compatible (type_reference)) {
     360            1 :                         error = true;
     361            1 :                         Report.error (source_reference, "Foreach: Cannot convert from `%s' to `%s'", element_type.to_string (), type_reference.to_string ());
     362            1 :                         return false;
     363           36 :                 } else if (element_type.is_disposable () && element_type.value_owned && !type_reference.value_owned) {
     364            1 :                         error = true;
     365            1 :                         Report.error (source_reference, "Foreach: Invalid assignment from owned expression to unowned variable");
     366            1 :                         return false;
     367              :                 }
     368              : 
     369           51 :                 return true;
     370              :         }
     371              : 
     372          171 :         bool check_without_iterator (CodeContext context, DataType collection_type, DataType element_type) {
     373              :                 // analyze element type
     374          171 :                 if (type_reference is VarType) {
     375              :                         // var type
     376           33 :                         bool nullable = type_reference.nullable;
     377           33 :                         bool value_owned = type_reference.value_owned;
     378           33 :                         bool is_dynamic = type_reference.is_dynamic;
     379           33 :                         type_reference = element_type.copy ();
     380              :                         // FIXME Only follows "unowned var" otherwise inherit ownership of element-type
     381           33 :                         if (!value_owned) {
     382            5 :                                 type_reference.value_owned = false;
     383              :                         }
     384           33 :                         if (nullable) {
     385            5 :                                 type_reference.nullable = true;
     386              :                         }
     387           33 :                         if (is_dynamic) {
     388            4 :                                 type_reference.is_dynamic = true;
     389              :                         }
     390          138 :                 } else if (!element_type.compatible (type_reference)) {
     391            4 :                         error = true;
     392            4 :                         Report.error (source_reference, "Foreach: Cannot convert from `%s' to `%s'", element_type.to_string (), type_reference.to_string ());
     393            4 :                         return false;
     394              :                 }
     395              : 
     396          167 :                 element_variable = new LocalVariable (type_reference, variable_name, null, source_reference);
     397              : 
     398          167 :                 body.scope.add (variable_name, element_variable);
     399              : 
     400          167 :                 body.add_local_variable (element_variable);
     401          167 :                 element_variable.active = true;
     402          167 :                 element_variable.checked = true;
     403              : 
     404              :                 // analyze body
     405          167 :                 var old_symbol = context.analyzer.current_symbol;
     406          167 :                 owner = context.analyzer.current_symbol.scope;
     407          167 :                 context.analyzer.current_symbol = this;
     408              : 
     409              :                 // call add_local_variable to check for shadowed variable
     410          167 :                 add_local_variable (element_variable);
     411          167 :                 remove_local_variable (element_variable);
     412              : 
     413          167 :                 body.check (context);
     414              : 
     415          167 :                 foreach (LocalVariable local in get_local_variables ()) {
     416            0 :                         local.active = false;
     417              :                 }
     418              : 
     419          167 :                 context.analyzer.current_symbol = old_symbol;
     420              : 
     421          167 :                 collection_variable = new LocalVariable (collection_type.copy (), "%s_collection".printf (variable_name), null, source_reference);
     422              : 
     423          167 :                 add_local_variable (collection_variable);
     424          167 :                 collection_variable.active = true;
     425              : 
     426          167 :                 return !error;
     427              :         }
     428              : 
     429          814 :         public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
     430          814 :                 if (source_reference == null) {
     431          771 :                         source_reference = this.source_reference;
     432              :                 }
     433          814 :                 this.collection.get_error_types (collection, source_reference);
     434          814 :                 body.get_error_types (collection, source_reference);
     435              :         }
     436              : 
     437          635 :         public override void emit (CodeGenerator codegen) {
     438          635 :                 if (use_iterator) {
     439          468 :                         base.emit (codegen);
     440          468 :                         return;
     441              :                 }
     442              : 
     443          167 :                 collection.emit (codegen);
     444          167 :                 codegen.visit_end_full_expression (collection);
     445              : 
     446          167 :                 element_variable.active = true;
     447          167 :                 collection_variable.active = true;
     448          167 :                 if (iterator_variable != null) {
     449            0 :                         iterator_variable.active = true;
     450              :                 }
     451              : 
     452          167 :                 codegen.visit_foreach_statement (this);
     453              :         }
     454              : 
     455          501 :         public override void get_defined_variables (Collection<Variable> collection) {
     456          501 :                 if (element_variable != null) {
     457          501 :                         collection.add (element_variable);
     458              :                 }
     459              :         }
     460              : }
        

Generated by: LCOV version 2.0-1