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

            Line data    Source code
       1              : /* valaexpression.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              :  * Base class for all code nodes that might be used as an expression.
      27              :  */
      28      8903884 : public abstract class Vala.Expression : CodeNode {
      29              :         /**
      30              :          * The static type of the value of this expression.
      31              :          *
      32              :          * The semantic analyzer computes this value.
      33              :          */
      34     38739911 :         public DataType value_type { get; set; }
      35              : 
      36     10717896 :         public DataType? formal_value_type { get; set; }
      37              : 
      38              :         /*
      39              :          * The static type this expression is expected to have.
      40              :          *
      41              :          * The semantic analyzer computes this value, lambda expressions use it.
      42              :          */
      43     14094680 :         public DataType target_type { get; set; }
      44              : 
      45      6260190 :         public DataType? formal_target_type { get; set; }
      46              : 
      47              :         /**
      48              :          * The symbol this expression refers to.
      49              :          */
      50     63457513 :         public weak Symbol symbol_reference { get; set; }
      51              : 
      52              :         /**
      53              :          * Specifies that this expression is used as lvalue, i.e. the
      54              :          * left hand side of an assignment.
      55              :          */
      56      2338737 :         public bool lvalue { get; set; }
      57              : 
      58      7005504 :         public TargetValue? target_value { get; set; }
      59              : 
      60              :         /**
      61              :          * Returns whether this expression is constant, i.e. whether this
      62              :          * expression only consists of literals and other constants.
      63              :          */
      64        32366 :         public virtual bool is_constant () {
      65           11 :                 return false;
      66              :         }
      67              : 
      68              :         /**
      69              :          * Returns whether this expression is pure, i.e. whether this expression
      70              :          * is free of side-effects.
      71              :          */
      72            0 :         public abstract bool is_pure ();
      73              : 
      74              :         /**
      75              :          * Returns whether this expression is guaranteed to be non-null.
      76              :          */
      77       148504 :         public virtual bool is_non_null () {
      78        28591 :                 return false;
      79              :         }
      80              : 
      81              :         /**
      82              :          * Check whether symbol_references in this expression are at least
      83              :          * as accessible as the specified symbol.
      84              :          */
      85      2129592 :         public virtual bool is_accessible (Symbol sym) {
      86              :                 return true;
      87              :         }
      88              : 
      89              :         /**
      90              :          * Check whether this expression is always true.
      91              :          */
      92       109310 :         public bool is_always_true () {
      93       109310 :                 unowned BooleanLiteral? literal = this as BooleanLiteral;
      94           50 :                 return (literal != null && literal.value);
      95              :         }
      96              : 
      97              :         /**
      98              :          * Check whether this expression is always false.
      99              :          */
     100       109274 :         public bool is_always_false () {
     101       109274 :                 unowned BooleanLiteral? literal = this as BooleanLiteral;
     102           19 :                 return (literal != null && !literal.value);
     103              :         }
     104              : 
     105              :         public Statement? parent_statement {
     106       134155 :                 get {
     107       134155 :                         if (parent_node is MemberInitializer) {
     108            2 :                                 return ((Expression) parent_node.parent_node).parent_statement;
     109       134153 :                         } else if (parent_node is LocalVariable) {
     110         3173 :                                 return (Statement) parent_node.parent_node;
     111       130980 :                         } else if (parent_node is Statement) {
     112        90052 :                                 return (Statement) parent_node;
     113        40928 :                         } else if (parent_node is Expression) {
     114        40928 :                                 return ((Expression) parent_node).parent_statement;
     115              :                         } else {
     116            0 :                                 return null;
     117              :                         }
     118              :                 }
     119              :         }
     120              : 
     121        93225 :         public void insert_statement (Block block, Statement stmt) {
     122        93225 :                 block.insert_before (parent_statement, stmt);
     123              :         }
     124              : 
     125           25 :         public override bool check (CodeContext context) {
     126              :                 //Add null checks to a null-safe expression
     127              : 
     128           25 :                 unowned MethodCall? call = this as MethodCall;
     129            7 :                 unowned Expression access = call != null ? call.call : this;
     130           25 :                 unowned MemberAccess? member_access = access as MemberAccess;
     131           25 :                 unowned ElementAccess? elem_access = access as ElementAccess;
     132           25 :                 unowned SliceExpression? slice_expr = access as SliceExpression;
     133              : 
     134           25 :                 unowned Expression? inner = null;
     135           25 :                 if (member_access != null && member_access.null_safe_access) {
     136           20 :                         inner = member_access.inner;
     137            5 :                 } else if (elem_access != null && elem_access.null_safe_access) {
     138            3 :                         inner = elem_access.container;
     139            2 :                 } else if (slice_expr != null && slice_expr.null_safe_access) {
     140            2 :                         inner = slice_expr.container;
     141              :                 } else {
     142              :                         // Nothing to do here
     143            0 :                         return true;
     144              :                 }
     145              : 
     146              :                 // get the type of the inner expression
     147           25 :                 if (!inner.check (context)) {
     148            0 :                         error = true;
     149            0 :                         return false;
     150              :                 }
     151              : 
     152              :                 // the inner expression may have been replaced by the check, reload it
     153           25 :                 if (member_access != null) {
     154           20 :                         inner = member_access.inner;
     155            5 :                 } else if (elem_access != null) {
     156            3 :                         inner = elem_access.container;
     157            2 :                 } else if (slice_expr != null) {
     158            2 :                         inner = slice_expr.container;
     159              :                 }
     160              : 
     161           25 :                 if (inner.value_type == null) {
     162            0 :                         Report.error (inner.source_reference, "invalid inner expression");
     163            0 :                         return false;
     164              :                 }
     165              : 
     166              :                 // declare the inner expression as a local variable to check for null
     167           25 :                 var inner_type = inner.value_type.copy ();
     168           25 :                 if (context.experimental_non_null && !inner_type.nullable) {
     169            0 :                         Report.warning (inner.source_reference, "inner expression is never null");
     170              :                         // make it nullable, otherwise the null check will not compile in non-null mode
     171            0 :                         inner_type.nullable = true;
     172              :                 }
     173           25 :                 var inner_local = new LocalVariable (inner_type, get_temp_name (), inner, inner.source_reference);
     174              : 
     175           25 :                 var inner_decl = new DeclarationStatement (inner_local, inner.source_reference);
     176           25 :                 insert_statement (context.analyzer.insert_block, inner_decl);
     177              : 
     178           25 :                 if (!inner_decl.check (context)) {
     179            0 :                         return false;
     180              :                 }
     181              : 
     182              :                 // create an equivalent non null-safe expression
     183           25 :                 Expression? non_null_expr = null;
     184              : 
     185           25 :                 Expression inner_access = new MemberAccess.simple (inner_local.name, source_reference);
     186              : 
     187           25 :                 if (context.experimental_non_null) {
     188            0 :                         inner_access = new CastExpression.non_null (inner_access, source_reference);
     189              :                 }
     190              : 
     191           25 :                 if (member_access != null) {
     192           20 :                         non_null_expr = new MemberAccess (inner_access, member_access.member_name, source_reference);
     193            5 :                 } else if (elem_access != null) {
     194            3 :                         var non_null_elem_access = new ElementAccess (inner_access, source_reference);
     195            9 :                         foreach (Expression index in elem_access.get_indices ()) {
     196            3 :                                 non_null_elem_access.append_index (index);
     197              :                         }
     198            6 :                         non_null_expr = non_null_elem_access;
     199            2 :                 } else if (slice_expr != null) {
     200            2 :                         non_null_expr = new SliceExpression (inner_access, slice_expr.start, slice_expr.stop, source_reference);
     201              :                 }
     202              : 
     203           25 :                 if ((member_access != null || elem_access != null)
     204           23 :                     && access.parent_node is ReferenceTransferExpression) {
     205              :                         // preserve ownership transfer
     206            2 :                         non_null_expr = new ReferenceTransferExpression (non_null_expr, source_reference);
     207              :                 }
     208              : 
     209           25 :                 if (!non_null_expr.check (context)) {
     210            0 :                         return false;
     211              :                 }
     212              : 
     213           25 :                 if (non_null_expr.value_type == null) {
     214            0 :                         Report.error (source_reference, "invalid null-safe expression");
     215            0 :                         error = true;
     216            0 :                         return false;
     217              :                 }
     218              : 
     219              :                 DataType result_type;
     220              : 
     221           32 :                 if (call != null) {
     222              :                         // if the expression is a method call, create an equivalent non-conditional method call
     223            7 :                         var non_null_call = new MethodCall (non_null_expr, source_reference);
     224            9 :                         foreach (Expression arg in call.get_argument_list ()) {
     225            1 :                                 non_null_call.add_argument (arg);
     226              :                         }
     227            7 :                         result_type = non_null_expr.value_type.get_return_type ().copy ();
     228            7 :                         non_null_expr = non_null_call;
     229              :                 } else {
     230           18 :                         result_type = non_null_expr.value_type.copy ();
     231              :                 }
     232              : 
     233           25 :                 if (result_type is VoidType) {
     234              :                         // void result type, replace the parent expression statement by a conditional statement
     235            1 :                         var non_null_stmt = new ExpressionStatement (non_null_expr, source_reference);
     236            1 :                         var non_null_block = new Block (source_reference);
     237            1 :                         non_null_block.add_statement (non_null_stmt);
     238              : 
     239            1 :                         var non_null_safe = new BinaryExpression (BinaryOperator.INEQUALITY, new MemberAccess.simple (inner_local.name, source_reference), new NullLiteral (source_reference), source_reference);
     240            1 :                         var non_null_ifstmt = new IfStatement (non_null_safe, non_null_block, null, source_reference);
     241              : 
     242            1 :                         unowned ExpressionStatement? parent_stmt = parent_node as ExpressionStatement;
     243            1 :                         unowned Block? parent_block = parent_stmt != null ? parent_stmt.parent_node as Block : null;
     244              : 
     245            1 :                         if (parent_stmt == null || parent_block == null) {
     246            0 :                                 Report.error (source_reference, "void method call not allowed here");
     247            0 :                                 error = true;
     248            0 :                                 return false;
     249              :                         }
     250              : 
     251            1 :                         context.analyzer.replaced_nodes.add (parent_stmt);
     252            1 :                         parent_block.replace_statement (parent_stmt, non_null_ifstmt);
     253            1 :                         return non_null_ifstmt.check (context);
     254              :                 } else {
     255              :                         // non-void result type, replace the expression by an access to a local variable
     256           24 :                         if (!result_type.nullable) {
     257           20 :                                 if (result_type is ValueType) {
     258              :                                         // the value must be owned, otherwise the local variable may receive a stale pointer to the stack
     259           15 :                                         result_type.value_owned = true;
     260              :                                 }
     261           20 :                                 result_type.nullable = true;
     262              :                         }
     263           24 :                         var result_local = new LocalVariable (result_type, get_temp_name (), new NullLiteral (source_reference), source_reference);
     264              : 
     265           24 :                         var result_decl = new DeclarationStatement (result_local, source_reference);
     266           24 :                         insert_statement (context.analyzer.insert_block, result_decl);
     267              : 
     268           24 :                         if (!result_decl.check (context)) {
     269            0 :                                 return false;
     270              :                         }
     271              : 
     272              :                         // assign the non-conditional member access if the inner expression is not null
     273           24 :                         var non_null_safe = new BinaryExpression (BinaryOperator.INEQUALITY, new MemberAccess.simple (inner_local.name, source_reference), new NullLiteral (source_reference), source_reference);
     274           24 :                         var non_null_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (result_local.name, source_reference), non_null_expr, AssignmentOperator.SIMPLE, source_reference), source_reference);
     275           24 :                         var non_null_block = new Block (source_reference);
     276           24 :                         non_null_block.add_statement (non_null_stmt);
     277           24 :                         var non_null_ifstmt = new IfStatement (non_null_safe, non_null_block, null, source_reference);
     278           24 :                         insert_statement (context.analyzer.insert_block, non_null_ifstmt);
     279              : 
     280           24 :                         if (!non_null_ifstmt.check (context)) {
     281            0 :                                 return false;
     282              :                         }
     283              : 
     284           24 :                         var result_access = SemanticAnalyzer.create_temp_access (result_local, target_type);
     285           24 :                         context.analyzer.replaced_nodes.add (this);
     286           24 :                         parent_node.replace_expression (this, result_access);
     287              : 
     288           24 :                         if (lvalue) {
     289            3 :                                 if (non_null_expr is ReferenceTransferExpression) {
     290              :                                         // ownership can be transferred transitively
     291            2 :                                         result_access.lvalue = true;
     292              :                                 } else {
     293            1 :                                         Report.error (source_reference, "null-safe expression not supported as lvalue");
     294            1 :                                         error = true;
     295            1 :                                         return false;
     296              :                                 }
     297              :                         }
     298              : 
     299           23 :                         return result_access.check (context);
     300              :                 }
     301              :         }
     302              : }
        

Generated by: LCOV version 2.0-1