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