valaccodeassignmentmodule.vala 10.3 KB
Newer Older
1
/* valaccodeassignmentmodule.vala
2
 *
3
 * Copyright (C) 2006-2010  Jürg Billeter
4
 * Copyright (C) 2006-2008  Raffaele Sandrini
5 6 7 8
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10 11 12 13 14 15 16 17 18 19 20 21

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 * 	Jürg Billeter <j@bitron.ch>
22
 *	Raffaele Sandrini <raffaele@sandrini.ch>
23 24 25 26
 */

using GLib;

27 28 29
/**
 * The link between an assignment and generated code.
 */
30
public class Vala.CCodeAssignmentModule : CCodeMemberAccessModule {
31
	CCodeExpression emit_property_assignment (Assignment assignment) {
Jürg Billeter's avatar
Jürg Billeter committed
32
		var ma = assignment.left as MemberAccess;
33

Jürg Billeter's avatar
Jürg Billeter committed
34
		var prop = (Property) assignment.left.symbol_reference;
35

36 37 38 39 40 41 42 43 44 45 46 47
		if (!(prop is DynamicProperty)) {
			generate_property_accessor_declaration (prop.set_accessor, source_declarations);

			if (!prop.external && prop.external_package) {
				// internal VAPI properties
				// only add them once per source file
				if (add_generated_external_symbol (prop)) {
					visit_property (prop);
				}
			}
		}

48
		if (prop.set_accessor.construction && current_type_symbol is Class && current_class.is_subtype_of (gobject_type) && in_creation_method) {
49
			return get_construct_property_assignment (prop.get_canonical_cconstant (), prop.property_type, (CCodeExpression) assignment.right.ccodenode);
Jürg Billeter's avatar
Jürg Billeter committed
50 51 52 53
		} else {
			CCodeExpression cexpr = (CCodeExpression) assignment.right.ccodenode;

			if (!prop.no_accessor_method) {
54
				if (prop.property_type.is_real_non_null_struct_type ()) {
55
					cexpr = get_address_of_expression (assignment.right, cexpr);
56
				}
Jürg Billeter's avatar
Jürg Billeter committed
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
			}

			if (assignment.operator != AssignmentOperator.SIMPLE) {
				CCodeBinaryOperator cop;
				if (assignment.operator == AssignmentOperator.BITWISE_OR) {
					cop = CCodeBinaryOperator.BITWISE_OR;
				} else if (assignment.operator == AssignmentOperator.BITWISE_AND) {
					cop = CCodeBinaryOperator.BITWISE_AND;
				} else if (assignment.operator == AssignmentOperator.BITWISE_XOR) {
					cop = CCodeBinaryOperator.BITWISE_XOR;
				} else if (assignment.operator == AssignmentOperator.ADD) {
					cop = CCodeBinaryOperator.PLUS;
				} else if (assignment.operator == AssignmentOperator.SUB) {
					cop = CCodeBinaryOperator.MINUS;
				} else if (assignment.operator == AssignmentOperator.MUL) {
					cop = CCodeBinaryOperator.MUL;
				} else if (assignment.operator == AssignmentOperator.DIV) {
					cop = CCodeBinaryOperator.DIV;
				} else if (assignment.operator == AssignmentOperator.PERCENT) {
					cop = CCodeBinaryOperator.MOD;
				} else if (assignment.operator == AssignmentOperator.SHIFT_LEFT) {
					cop = CCodeBinaryOperator.SHIFT_LEFT;
				} else if (assignment.operator == AssignmentOperator.SHIFT_RIGHT) {
					cop = CCodeBinaryOperator.SHIFT_RIGHT;
81 82
				} else {
					assert_not_reached ();
Jürg Billeter's avatar
Jürg Billeter committed
83
				}
84
				cexpr = new CCodeBinaryExpression (cop, (CCodeExpression) get_ccodenode (assignment.left), cexpr);
Jürg Billeter's avatar
Jürg Billeter committed
85 86
			}
			
87
			var ccall = get_property_set_call (prop, ma, cexpr, assignment.right);
Jürg Billeter's avatar
Jürg Billeter committed
88 89 90
			
			// assignments are expressions, so return the current property value, except if we're sure that it can't be used
			if (!(assignment.parent_node is ExpressionStatement)) {
91
				var ccomma = new CCodeCommaExpression ();
Jürg Billeter's avatar
Jürg Billeter committed
92
				ccomma.append_expression (ccall); // update property
93
				ccomma.append_expression ((CCodeExpression) get_ccodenode (ma)); // current property value
94
				
95
				return ccomma;
96
			} else {
97
				return ccall;
Jürg Billeter's avatar
Jürg Billeter committed
98 99 100
			}
		}
	}
101

102
	CCodeExpression emit_simple_assignment (Assignment assignment) {
Jürg Billeter's avatar
Jürg Billeter committed
103
		CCodeExpression rhs = (CCodeExpression) assignment.right.ccodenode;
104 105
		CCodeExpression lhs = (CCodeExpression) get_ccodenode (assignment.left);
		CCodeCommaExpression outer_ccomma = null;
106

107
		bool unref_old = requires_destroy (assignment.left.value_type);
Jürg Billeter's avatar
Jürg Billeter committed
108
		bool array = false;
109
		bool instance_delegate = false;
110
		if (assignment.left.value_type is ArrayType) {
111 112
			var array_field = assignment.left.symbol_reference as Field;
			array = (array_field == null || !array_field.no_array_length);
113 114
		} else if (assignment.left.value_type is DelegateType) {
			var delegate_type = (DelegateType) assignment.left.value_type;
115 116 117 118 119 120
			if (delegate_type.delegate_symbol.has_target) {
				var delegate_field = assignment.left.symbol_reference as Field;
				if (delegate_field == null || !delegate_field.no_delegate_target) {
					instance_delegate = true;
				}
			}
Jürg Billeter's avatar
Jürg Billeter committed
121
		}
122

123
		if (unref_old || array || instance_delegate) {
Jürg Billeter's avatar
Jürg Billeter committed
124
			var ccomma = new CCodeCommaExpression ();
125 126 127 128 129 130

			if (!is_pure_ccode_expression (lhs)) {
				/* Assign lhs to temp var to avoid repeating side effect */
				outer_ccomma = new CCodeCommaExpression ();

				var lhs_value_type = assignment.left.value_type.copy ();
131
				string lhs_temp_name = "_tmp%d_".printf (next_temp_var_id++);
132
				var lhs_temp = new LocalVariable (lhs_value_type, "*" + lhs_temp_name);
133
				temp_vars.add (lhs_temp);
134 135 136 137
				outer_ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (lhs_temp_name), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, lhs)));
				lhs = new CCodeParenthesizedExpression (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_variable_cexpression (lhs_temp_name)));
			}

138
			var temp_decl = get_temp_variable (assignment.left.value_type, true, null, false);
139
			temp_vars.add (temp_decl);
Jürg Billeter's avatar
Jürg Billeter committed
140
			ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (temp_decl.name), rhs));
Jürg Billeter's avatar
Jürg Billeter committed
141 142
			if (unref_old) {
				/* unref old value */
143
				ccomma.append_expression (get_unref_expression (lhs, assignment.left.value_type, assignment.left));
144 145
			}
			
Jürg Billeter's avatar
Jürg Billeter committed
146
			if (array) {
147
				var array_type = (ArrayType) assignment.left.value_type;
Jürg Billeter's avatar
Jürg Billeter committed
148
				for (int dim = 1; dim <= array_type.rank; dim++) {
149 150
					var lhs_array_len = get_array_length_cexpression (assignment.left, dim);
					var rhs_array_len = get_array_length_cexpression (assignment.right, dim);
Jürg Billeter's avatar
Jürg Billeter committed
151
					ccomma.append_expression (new CCodeAssignment (lhs_array_len, rhs_array_len));
152
				}
153 154
				if (array_type.rank == 1) {
					var array_var = assignment.left.symbol_reference;
155
					var array_local = array_var as LocalVariable;
156
					if (array_var != null && array_var.is_internal_symbol ()
157
					    && ((array_var is LocalVariable && !array_local.captured) || array_var is Field)) {
158 159
						var lhs_array_size = get_array_size_cexpression (assignment.left);
						var rhs_array_len = get_array_length_cexpression (assignment.left, 1);
160 161 162
						ccomma.append_expression (new CCodeAssignment (lhs_array_size, rhs_array_len));
					}
				}
163
			} else if (instance_delegate) {
164 165 166
				CCodeExpression lhs_delegate_target_destroy_notify, rhs_delegate_target_destroy_notify;
				var lhs_delegate_target = get_delegate_target_cexpression (assignment.left, out lhs_delegate_target_destroy_notify);
				var rhs_delegate_target = get_delegate_target_cexpression (assignment.right, out rhs_delegate_target_destroy_notify);
167
				ccomma.append_expression (new CCodeAssignment (lhs_delegate_target, rhs_delegate_target));
168 169 170
				if (assignment.right.target_type.value_owned) {
					ccomma.append_expression (new CCodeAssignment (lhs_delegate_target_destroy_notify, rhs_delegate_target_destroy_notify));
				}
171 172
			}
			
Jürg Billeter's avatar
Jürg Billeter committed
173
			ccomma.append_expression (get_variable_cexpression (temp_decl.name));
Jürg Billeter's avatar
Jürg Billeter committed
174 175 176
			
			rhs = ccomma;
		}
177
		
Jürg Billeter's avatar
Jürg Billeter committed
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
		var cop = CCodeAssignmentOperator.SIMPLE;
		if (assignment.operator == AssignmentOperator.BITWISE_OR) {
			cop = CCodeAssignmentOperator.BITWISE_OR;
		} else if (assignment.operator == AssignmentOperator.BITWISE_AND) {
			cop = CCodeAssignmentOperator.BITWISE_AND;
		} else if (assignment.operator == AssignmentOperator.BITWISE_XOR) {
			cop = CCodeAssignmentOperator.BITWISE_XOR;
		} else if (assignment.operator == AssignmentOperator.ADD) {
			cop = CCodeAssignmentOperator.ADD;
		} else if (assignment.operator == AssignmentOperator.SUB) {
			cop = CCodeAssignmentOperator.SUB;
		} else if (assignment.operator == AssignmentOperator.MUL) {
			cop = CCodeAssignmentOperator.MUL;
		} else if (assignment.operator == AssignmentOperator.DIV) {
			cop = CCodeAssignmentOperator.DIV;
		} else if (assignment.operator == AssignmentOperator.PERCENT) {
			cop = CCodeAssignmentOperator.PERCENT;
		} else if (assignment.operator == AssignmentOperator.SHIFT_LEFT) {
			cop = CCodeAssignmentOperator.SHIFT_LEFT;
		} else if (assignment.operator == AssignmentOperator.SHIFT_RIGHT) {
			cop = CCodeAssignmentOperator.SHIFT_RIGHT;
		}

201 202 203 204 205
		CCodeExpression codenode = new CCodeAssignment (lhs, rhs, cop);

		if (outer_ccomma != null) {
			outer_ccomma.append_expression (codenode);
			codenode = outer_ccomma;
206
		}
207 208

		return codenode;
Jürg Billeter's avatar
Jürg Billeter committed
209 210
	}

211 212 213 214
	CCodeExpression emit_fixed_length_array_assignment (Assignment assignment, ArrayType array_type) {
		CCodeExpression rhs = (CCodeExpression) assignment.right.ccodenode;
		CCodeExpression lhs = (CCodeExpression) get_ccodenode (assignment.left);

215 216
		source_declarations.add_include ("string.h");

217 218 219 220 221 222 223 224 225 226 227 228 229
		// it is necessary to use memcpy for fixed-length (stack-allocated) arrays
		// simple assignments do not work in C
		var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
		sizeof_call.add_argument (new CCodeIdentifier (array_type.element_type.get_cname ()));
		var size = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeConstant ("%d".printf (array_type.length)), sizeof_call);
		var ccopy = new CCodeFunctionCall (new CCodeIdentifier ("memcpy"));
		ccopy.add_argument (lhs);
		ccopy.add_argument (rhs);
		ccopy.add_argument (size);

		return ccopy;
	}

230
	public override void visit_assignment (Assignment assignment) {
231 232 233 234 235
		if (assignment.left.error || assignment.right.error) {
			assignment.error = true;
			return;
		}

Jürg Billeter's avatar
Jürg Billeter committed
236
		if (assignment.left.symbol_reference is Property) {
237
			assignment.ccodenode = emit_property_assignment (assignment);
Jürg Billeter's avatar
Jürg Billeter committed
238
		} else {
239 240 241 242 243 244
			var array_type = assignment.left.value_type as ArrayType;
			if (array_type != null && array_type.fixed_length) {
				assignment.ccodenode = emit_fixed_length_array_assignment (assignment, array_type);
			} else {
				assignment.ccodenode = emit_simple_assignment (assignment);
			}
Jürg Billeter's avatar
Jürg Billeter committed
245
		}
246 247
	}
}