1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "tools/gn/operators.h"
6
7 #include "base/strings/string_number_conversions.h"
8 #include "tools/gn/err.h"
9 #include "tools/gn/parse_tree.h"
10 #include "tools/gn/scope.h"
11 #include "tools/gn/token.h"
12 #include "tools/gn/value.h"
13
14 namespace {
15
16 const char kSourcesName[] = "sources";
17
18 // Applies the sources assignment filter from the given scope to each element
19 // of source (can be a list or a string), appending it to dest if it doesn't
20 // match.
AppendFilteredSourcesToValue(const Scope * scope,const Value & source,Value * dest)21 void AppendFilteredSourcesToValue(const Scope* scope,
22 const Value& source,
23 Value* dest) {
24 const PatternList* filter = scope->GetSourcesAssignmentFilter();
25
26 const std::vector<Value>& source_list = source.list_value();
27
28 if (source.type() == Value::STRING) {
29 if (!filter || filter->is_empty() ||
30 !filter->MatchesValue(source))
31 dest->list_value().push_back(source);
32 return;
33 }
34
35 // Otherwise source is a list.
36 DCHECK(source.type() == Value::LIST);
37 if (!filter || filter->is_empty()) {
38 // No filter, append everything.
39 for (size_t i = 0; i < source_list.size(); i++)
40 dest->list_value().push_back(source_list[i]);
41 return;
42 }
43
44 // Note: don't reserve() the dest vector here since that actually hurts
45 // the allocation pattern when the build script is doing multiple small
46 // additions.
47 for (size_t i = 0; i < source_list.size(); i++) {
48 if (!filter->MatchesValue(source_list[i]))
49 dest->list_value().push_back(source_list[i]);
50 }
51 }
52
RemoveMatchesFromList(const BinaryOpNode * op_node,Value * list,const Value & to_remove,Err * err)53 void RemoveMatchesFromList(const BinaryOpNode* op_node,
54 Value* list,
55 const Value& to_remove,
56 Err* err) {
57 std::vector<Value>& v = list->list_value();
58 switch (to_remove.type()) {
59 case Value::BOOLEAN:
60 case Value::INTEGER: // Filter out the individual int/string.
61 case Value::STRING: {
62 bool found_match = false;
63 for (size_t i = 0; i < v.size(); /* nothing */) {
64 if (v[i] == to_remove) {
65 found_match = true;
66 v.erase(v.begin() + i);
67 } else {
68 i++;
69 }
70 }
71 if (!found_match) {
72 *err = Err(to_remove.origin()->GetRange(), "Item not found",
73 "You were trying to remove " + to_remove.ToString(true) +
74 "\nfrom the list but it wasn't there.");
75 }
76 break;
77 }
78
79 case Value::LIST: // Filter out each individual thing.
80 for (size_t i = 0; i < to_remove.list_value().size(); i++) {
81 // TODO(brettw) if the nested item is a list, we may want to search
82 // for the literal list rather than remote the items in it.
83 RemoveMatchesFromList(op_node, list, to_remove.list_value()[i], err);
84 if (err->has_error())
85 return;
86 }
87 break;
88
89 default:
90 break;
91 }
92 }
93
94 // Assignment -----------------------------------------------------------------
95
ExecuteEquals(Scope * scope,const BinaryOpNode * op_node,const Token & left,const Value & right,Err * err)96 Value ExecuteEquals(Scope* scope,
97 const BinaryOpNode* op_node,
98 const Token& left,
99 const Value& right,
100 Err* err) {
101 const Value* old_value = scope->GetValue(left.value(), false);
102 if (old_value) {
103 if (scope->IsSetButUnused(left.value())) {
104 // Throw an error for re-assigning without using the value first. The
105 // exception is that you can overwrite an empty list with another list
106 // since this is the way to get around the "can't overwrite a nonempty
107 // list with another nonempty list" restriction.
108 if (old_value->type() != Value::LIST ||
109 !old_value->list_value().empty()) {
110 *err = Err(op_node->left()->GetRange(), "Overwriting unused variable.",
111 "This overwrites a previous assignment to \"" +
112 left.value().as_string() + "\" that had no effect.");
113 err->AppendSubErr(Err(*scope->GetValue(left.value()),
114 "Previously set here.",
115 "Maybe you wanted \"+=\" to append instead?"));
116 return Value();
117 }
118 } else {
119 // Throw an error when overwriting a nonempty list with another nonempty
120 // list item. This is to detect the case where you write
121 // defines = ["FOO"]
122 // and you overwrote inherited ones, when instead you mean to append:
123 // defines += ["FOO"]
124 if (old_value->type() == Value::LIST &&
125 !old_value->list_value().empty() &&
126 right.type() == Value::LIST &&
127 !right.list_value().empty()) {
128 *err = Err(op_node->left()->GetRange(), "Replacing nonempty list.",
129 std::string("This overwrites a previously-defined nonempty list ") +
130 "(length " +
131 base::IntToString(static_cast<int>(old_value->list_value().size()))
132 + ").");
133 err->AppendSubErr(Err(*old_value, "for previous definition",
134 "with another one (length " +
135 base::IntToString(static_cast<int>(right.list_value().size())) +
136 "). Did you mean " +
137 "\"+=\" to append instead? If you\nreally want to do this, do\n " +
138 left.value().as_string() + " = []\nbefore reassigning."));
139 return Value();
140 }
141 }
142 }
143 if (err->has_error())
144 return Value();
145
146 if (right.type() == Value::LIST && left.value() == kSourcesName) {
147 // Assigning to sources, filter the list. Here we do the filtering and
148 // copying in one step to save an extra list copy (the lists may be
149 // long).
150 Value* set_value = scope->SetValue(left.value(),
151 Value(op_node, Value::LIST), op_node);
152 set_value->list_value().reserve(right.list_value().size());
153 AppendFilteredSourcesToValue(scope, right, set_value);
154 } else {
155 // Normal value set, just copy it.
156 scope->SetValue(left.value(), right, op_node->right());
157 }
158 return Value();
159 }
160
161 // allow_type_conversion indicates if we're allowed to change the type of the
162 // left value. This is set to true when doing +, and false when doing +=.
ValuePlusEquals(const Scope * scope,const BinaryOpNode * op_node,const Token & left_token,Value * left,const Value & right,bool allow_type_conversion,Err * err)163 void ValuePlusEquals(const Scope* scope,
164 const BinaryOpNode* op_node,
165 const Token& left_token,
166 Value* left,
167 const Value& right,
168 bool allow_type_conversion,
169 Err* err) {
170 switch (left->type()) {
171 // Left-hand-side int.
172 case Value::INTEGER:
173 switch (right.type()) {
174 case Value::INTEGER: // int + int -> addition.
175 left->int_value() += right.int_value();
176 return;
177
178 case Value::STRING: // int + string -> string concat.
179 if (allow_type_conversion) {
180 *left = Value(op_node,
181 base::Int64ToString(left->int_value()) + right.string_value());
182 return;
183 }
184 break;
185
186 default:
187 break;
188 }
189 break;
190
191 // Left-hand-side string.
192 case Value::STRING:
193 switch (right.type()) {
194 case Value::INTEGER: // string + int -> string concat.
195 left->string_value().append(base::Int64ToString(right.int_value()));
196 return;
197
198 case Value::STRING: // string + string -> string contat.
199 left->string_value().append(right.string_value());
200 return;
201
202 default:
203 break;
204 }
205 break;
206
207 // Left-hand-side list.
208 case Value::LIST:
209 switch (right.type()) {
210 case Value::INTEGER: // list + integer -> list append.
211 case Value::STRING: // list + string -> list append.
212 if (left_token.value() == kSourcesName)
213 AppendFilteredSourcesToValue(scope, right, left);
214 else
215 left->list_value().push_back(right);
216 return;
217
218 case Value::LIST: // list + list -> list concat.
219 if (left_token.value() == kSourcesName) {
220 // Filter additions through the assignment filter.
221 AppendFilteredSourcesToValue(scope, right, left);
222 } else {
223 // Normal list concat.
224 for (size_t i = 0; i < right.list_value().size(); i++)
225 left->list_value().push_back(right.list_value()[i]);
226 }
227 return;
228
229 default:
230 break;
231 }
232
233 default:
234 break;
235 }
236
237 *err = Err(op_node->op(), "Incompatible types to add.",
238 std::string("I see a ") + Value::DescribeType(left->type()) + " and a " +
239 Value::DescribeType(right.type()) + ".");
240 }
241
ExecutePlusEquals(Scope * scope,const BinaryOpNode * op_node,const Token & left,const Value & right,Err * err)242 Value ExecutePlusEquals(Scope* scope,
243 const BinaryOpNode* op_node,
244 const Token& left,
245 const Value& right,
246 Err* err) {
247 // We modify in-place rather than doing read-modify-write to avoid
248 // copying large lists.
249 Value* left_value =
250 scope->GetValueForcedToCurrentScope(left.value(), op_node);
251 if (!left_value) {
252 *err = Err(left, "Undefined variable for +=.",
253 "I don't have something with this name in scope now.");
254 return Value();
255 }
256 ValuePlusEquals(scope, op_node, left, left_value, right, false, err);
257 left_value->set_origin(op_node);
258 scope->MarkUnused(left.value());
259 return Value();
260 }
261
ValueMinusEquals(const BinaryOpNode * op_node,Value * left,const Value & right,bool allow_type_conversion,Err * err)262 void ValueMinusEquals(const BinaryOpNode* op_node,
263 Value* left,
264 const Value& right,
265 bool allow_type_conversion,
266 Err* err) {
267 switch (left->type()) {
268 // Left-hand-side int.
269 case Value::INTEGER:
270 switch (right.type()) {
271 case Value::INTEGER: // int - int -> subtraction.
272 left->int_value() -= right.int_value();
273 return;
274
275 default:
276 break;
277 }
278 break;
279
280 // Left-hand-side string.
281 case Value::STRING:
282 break; // All are errors.
283
284 // Left-hand-side list.
285 case Value::LIST:
286 RemoveMatchesFromList(op_node, left, right, err);
287 return;
288
289 default:
290 break;
291 }
292
293 *err = Err(op_node->op(), "Incompatible types to add.",
294 std::string("I see a ") + Value::DescribeType(left->type()) + " and a " +
295 Value::DescribeType(right.type()) + ".");
296 }
297
ExecuteMinusEquals(Scope * scope,const BinaryOpNode * op_node,const Token & left,const Value & right,Err * err)298 Value ExecuteMinusEquals(Scope* scope,
299 const BinaryOpNode* op_node,
300 const Token& left,
301 const Value& right,
302 Err* err) {
303 Value* left_value =
304 scope->GetValueForcedToCurrentScope(left.value(), op_node);
305 if (!left_value) {
306 *err = Err(left, "Undefined variable for -=.",
307 "I don't have something with this name in scope now.");
308 return Value();
309 }
310 ValueMinusEquals(op_node, left_value, right, false, err);
311 left_value->set_origin(op_node);
312 scope->MarkUnused(left.value());
313 return Value();
314 }
315
316 // Plus/Minus -----------------------------------------------------------------
317
ExecutePlus(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)318 Value ExecutePlus(Scope* scope,
319 const BinaryOpNode* op_node,
320 const Value& left,
321 const Value& right,
322 Err* err) {
323 Value ret = left;
324 ValuePlusEquals(scope, op_node, Token(), &ret, right, true, err);
325 ret.set_origin(op_node);
326 return ret;
327 }
328
ExecuteMinus(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)329 Value ExecuteMinus(Scope* scope,
330 const BinaryOpNode* op_node,
331 const Value& left,
332 const Value& right,
333 Err* err) {
334 Value ret = left;
335 ValueMinusEquals(op_node, &ret, right, true, err);
336 ret.set_origin(op_node);
337 return ret;
338 }
339
340 // Comparison -----------------------------------------------------------------
341
ExecuteEqualsEquals(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)342 Value ExecuteEqualsEquals(Scope* scope,
343 const BinaryOpNode* op_node,
344 const Value& left,
345 const Value& right,
346 Err* err) {
347 if (left == right)
348 return Value(op_node, true);
349 return Value(op_node, false);
350 }
351
ExecuteNotEquals(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)352 Value ExecuteNotEquals(Scope* scope,
353 const BinaryOpNode* op_node,
354 const Value& left,
355 const Value& right,
356 Err* err) {
357 // Evaluate in terms of ==.
358 Value result = ExecuteEqualsEquals(scope, op_node, left, right, err);
359 result.boolean_value() = !result.boolean_value();
360 return result;
361 }
362
FillNeedsTwoIntegersError(const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)363 Value FillNeedsTwoIntegersError(const BinaryOpNode* op_node,
364 const Value& left,
365 const Value& right,
366 Err* err) {
367 *err = Err(op_node, "Comparison requires two integers.",
368 "This operator can only compare two integers.");
369 err->AppendRange(left.origin()->GetRange());
370 err->AppendRange(right.origin()->GetRange());
371 return Value();
372 }
373
ExecuteLessEquals(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)374 Value ExecuteLessEquals(Scope* scope,
375 const BinaryOpNode* op_node,
376 const Value& left,
377 const Value& right,
378 Err* err) {
379 if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
380 return FillNeedsTwoIntegersError(op_node, left, right, err);
381 return Value(op_node, left.int_value() <= right.int_value());
382 }
383
ExecuteGreaterEquals(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)384 Value ExecuteGreaterEquals(Scope* scope,
385 const BinaryOpNode* op_node,
386 const Value& left,
387 const Value& right,
388 Err* err) {
389 if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
390 return FillNeedsTwoIntegersError(op_node, left, right, err);
391 return Value(op_node, left.int_value() >= right.int_value());
392 }
393
ExecuteGreater(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)394 Value ExecuteGreater(Scope* scope,
395 const BinaryOpNode* op_node,
396 const Value& left,
397 const Value& right,
398 Err* err) {
399 if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
400 return FillNeedsTwoIntegersError(op_node, left, right, err);
401 return Value(op_node, left.int_value() > right.int_value());
402 }
403
ExecuteLess(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)404 Value ExecuteLess(Scope* scope,
405 const BinaryOpNode* op_node,
406 const Value& left,
407 const Value& right,
408 Err* err) {
409 if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
410 return FillNeedsTwoIntegersError(op_node, left, right, err);
411 return Value(op_node, left.int_value() < right.int_value());
412 }
413
414 // Binary ----------------------------------------------------------------------
415
ExecuteOr(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)416 Value ExecuteOr(Scope* scope,
417 const BinaryOpNode* op_node,
418 const Value& left,
419 const Value& right,
420 Err* err) {
421 if (left.type() != Value::BOOLEAN) {
422 *err = Err(left, "Left side of || operator is not a boolean.");
423 err->AppendRange(op_node->GetRange());
424 } else if (right.type() != Value::BOOLEAN) {
425 *err = Err(right, "Right side of || operator is not a boolean.");
426 err->AppendRange(op_node->GetRange());
427 }
428 return Value(op_node, left.boolean_value() || right.boolean_value());
429 }
430
ExecuteAnd(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)431 Value ExecuteAnd(Scope* scope,
432 const BinaryOpNode* op_node,
433 const Value& left,
434 const Value& right,
435 Err* err) {
436 if (left.type() != Value::BOOLEAN) {
437 *err = Err(left, "Left side of && operator is not a boolean.");
438 err->AppendRange(op_node->GetRange());
439 } else if (right.type() != Value::BOOLEAN) {
440 *err = Err(right, "Right side of && operator is not a boolean.");
441 err->AppendRange(op_node->GetRange());
442 }
443 return Value(op_node, left.boolean_value() && right.boolean_value());
444 }
445
446 } // namespace
447
448 // ----------------------------------------------------------------------------
449
IsUnaryOperator(const Token & token)450 bool IsUnaryOperator(const Token& token) {
451 return token.type() == Token::BANG;
452 }
453
IsBinaryOperator(const Token & token)454 bool IsBinaryOperator(const Token& token) {
455 return token.type() == Token::EQUAL ||
456 token.type() == Token::PLUS ||
457 token.type() == Token::MINUS ||
458 token.type() == Token::PLUS_EQUALS ||
459 token.type() == Token::MINUS_EQUALS ||
460 token.type() == Token::EQUAL_EQUAL ||
461 token.type() == Token::NOT_EQUAL ||
462 token.type() == Token::LESS_EQUAL ||
463 token.type() == Token::GREATER_EQUAL ||
464 token.type() == Token::LESS_THAN ||
465 token.type() == Token::GREATER_THAN ||
466 token.type() == Token::BOOLEAN_AND ||
467 token.type() == Token::BOOLEAN_OR;
468 }
469
IsFunctionCallArgBeginScoper(const Token & token)470 bool IsFunctionCallArgBeginScoper(const Token& token) {
471 return token.type() == Token::LEFT_PAREN;
472 }
473
IsFunctionCallArgEndScoper(const Token & token)474 bool IsFunctionCallArgEndScoper(const Token& token) {
475 return token.type() == Token::RIGHT_PAREN;
476 }
477
IsScopeBeginScoper(const Token & token)478 bool IsScopeBeginScoper(const Token& token) {
479 return token.type() == Token::LEFT_BRACE;
480 }
481
IsScopeEndScoper(const Token & token)482 bool IsScopeEndScoper(const Token& token) {
483 return token.type() == Token::RIGHT_BRACE;
484 }
485
ExecuteUnaryOperator(Scope * scope,const UnaryOpNode * op_node,const Value & expr,Err * err)486 Value ExecuteUnaryOperator(Scope* scope,
487 const UnaryOpNode* op_node,
488 const Value& expr,
489 Err* err) {
490 DCHECK(op_node->op().type() == Token::BANG);
491
492 if (expr.type() != Value::BOOLEAN) {
493 *err = Err(expr, "Operand of ! operator is not a boolean.");
494 err->AppendRange(op_node->GetRange());
495 return Value();
496 }
497 // TODO(scottmg): Why no unary minus?
498 return Value(op_node, !expr.boolean_value());
499 }
500
ExecuteBinaryOperator(Scope * scope,const BinaryOpNode * op_node,const ParseNode * left,const ParseNode * right,Err * err)501 Value ExecuteBinaryOperator(Scope* scope,
502 const BinaryOpNode* op_node,
503 const ParseNode* left,
504 const ParseNode* right,
505 Err* err) {
506 const Token& op = op_node->op();
507
508 // First handle the ones that take an lvalue.
509 if (op.type() == Token::EQUAL ||
510 op.type() == Token::PLUS_EQUALS ||
511 op.type() == Token::MINUS_EQUALS) {
512 const IdentifierNode* left_id = left->AsIdentifier();
513 if (!left_id) {
514 *err = Err(op, "Operator requires an lvalue.",
515 "This thing on the left is not an idenfitier.");
516 err->AppendRange(left->GetRange());
517 return Value();
518 }
519 const Token& dest = left_id->value();
520
521 Value right_value = right->Execute(scope, err);
522 if (err->has_error())
523 return Value();
524 if (right_value.type() == Value::NONE) {
525 *err = Err(op, "Operator requires an rvalue.",
526 "This thing on the right does not evaluate to a value.");
527 err->AppendRange(right->GetRange());
528 return Value();
529 }
530
531 if (op.type() == Token::EQUAL)
532 return ExecuteEquals(scope, op_node, dest, right_value, err);
533 if (op.type() == Token::PLUS_EQUALS)
534 return ExecutePlusEquals(scope, op_node, dest, right_value, err);
535 if (op.type() == Token::MINUS_EQUALS)
536 return ExecuteMinusEquals(scope, op_node, dest, right_value, err);
537 NOTREACHED();
538 return Value();
539 }
540
541 // Left value.
542 Value left_value = left->Execute(scope, err);
543 if (err->has_error())
544 return Value();
545 if (left_value.type() == Value::NONE) {
546 *err = Err(op, "Operator requires an value.",
547 "This thing on the left does not evaluate to a value.");
548 err->AppendRange(left->GetRange());
549 return Value();
550 }
551
552 // Right value. Note: don't move this above to share code with the lvalue
553 // version since in this case we want to execute the left side first.
554 Value right_value = right->Execute(scope, err);
555 if (err->has_error())
556 return Value();
557 if (right_value.type() == Value::NONE) {
558 *err = Err(op, "Operator requires an value.",
559 "This thing on the right does not evaluate to a value.");
560 err->AppendRange(right->GetRange());
561 return Value();
562 }
563
564 // +, -.
565 if (op.type() == Token::MINUS)
566 return ExecuteMinus(scope, op_node, left_value, right_value, err);
567 if (op.type() == Token::PLUS)
568 return ExecutePlus(scope, op_node, left_value, right_value, err);
569
570 // Comparisons.
571 if (op.type() == Token::EQUAL_EQUAL)
572 return ExecuteEqualsEquals(scope, op_node, left_value, right_value, err);
573 if (op.type() == Token::NOT_EQUAL)
574 return ExecuteNotEquals(scope, op_node, left_value, right_value, err);
575 if (op.type() == Token::GREATER_EQUAL)
576 return ExecuteGreaterEquals(scope, op_node, left_value, right_value, err);
577 if (op.type() == Token::LESS_EQUAL)
578 return ExecuteLessEquals(scope, op_node, left_value, right_value, err);
579 if (op.type() == Token::GREATER_THAN)
580 return ExecuteGreater(scope, op_node, left_value, right_value, err);
581 if (op.type() == Token::LESS_THAN)
582 return ExecuteLess(scope, op_node, left_value, right_value, err);
583
584 // ||, &&.
585 if (op.type() == Token::BOOLEAN_OR)
586 return ExecuteOr(scope, op_node, left_value, right_value, err);
587 if (op.type() == Token::BOOLEAN_AND)
588 return ExecuteAnd(scope, op_node, left_value, right_value, err);
589
590 return Value();
591 }
592