• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gn/operators.h"
6 
7 #include <stdint.h>
8 
9 #include <memory>
10 #include <utility>
11 
12 #include "gn/parse_tree.h"
13 #include "gn/pattern.h"
14 #include "gn/test_with_scope.h"
15 #include "util/test/test.h"
16 
17 namespace {
18 
IsValueIntegerEqualing(const Value & v,int64_t i)19 bool IsValueIntegerEqualing(const Value& v, int64_t i) {
20   if (v.type() != Value::INTEGER)
21     return false;
22   return v.int_value() == i;
23 }
24 
IsValueStringEqualing(const Value & v,const char * s)25 bool IsValueStringEqualing(const Value& v, const char* s) {
26   if (v.type() != Value::STRING)
27     return false;
28   return v.string_value() == s;
29 }
30 
31 // This parse node is for passing to tests. It returns a canned value for
32 // Execute().
33 class TestParseNode : public ParseNode {
34  public:
TestParseNode(const Value & v)35   TestParseNode(const Value& v) : value_(v) {}
36 
Execute(Scope * scope,Err * err) const37   Value Execute(Scope* scope, Err* err) const override { return value_; }
GetRange() const38   LocationRange GetRange() const override { return LocationRange(); }
MakeErrorDescribing(const std::string & msg,const std::string & help) const39   Err MakeErrorDescribing(const std::string& msg,
40                           const std::string& help) const override {
41     return Err(this, msg);
42   }
GetJSONNode() const43   base::Value GetJSONNode() const override { return base::Value(); }
44 
45  private:
46   Value value_;
47 };
48 
49 // Sets up a BinaryOpNode for testing.
50 class TestBinaryOpNode : public BinaryOpNode {
51  public:
52   // Input token value string must outlive class.
TestBinaryOpNode(Token::Type op_token_type,const char * op_token_value)53   TestBinaryOpNode(Token::Type op_token_type, const char* op_token_value)
54       : BinaryOpNode(),
55         op_token_ownership_(Location(), op_token_type, op_token_value) {
56     set_op(op_token_ownership_);
57   }
58 
SetLeftToValue(const Value & value)59   void SetLeftToValue(const Value& value) {
60     set_left(std::make_unique<TestParseNode>(value));
61   }
62 
63   // Sets the left-hand side of the operator to an identifier node, this is
64   // used for testing assignments. Input string must outlive class.
SetLeftToIdentifier(const char * identifier)65   void SetLeftToIdentifier(const char* identifier) {
66     left_identifier_token_ownership_ =
67         Token(Location(), Token::IDENTIFIER, identifier);
68     set_left(
69         std::make_unique<IdentifierNode>(left_identifier_token_ownership_));
70   }
71 
SetRightToValue(const Value & value)72   void SetRightToValue(const Value& value) {
73     set_right(std::make_unique<TestParseNode>(value));
74   }
SetRightToListOfValue(const Value & value)75   void SetRightToListOfValue(const Value& value) {
76     Value list(nullptr, Value::LIST);
77     list.list_value().push_back(value);
78     set_right(std::make_unique<TestParseNode>(list));
79   }
SetRightToListOfValue(const Value & value1,const Value & value2)80   void SetRightToListOfValue(const Value& value1, const Value& value2) {
81     Value list(nullptr, Value::LIST);
82     list.list_value().push_back(value1);
83     list.list_value().push_back(value2);
84     set_right(std::make_unique<TestParseNode>(list));
85   }
86 
87  private:
88   // The base class takes the Token by reference, this manages the lifetime.
89   Token op_token_ownership_;
90 
91   // When setting the left to an identifier, this manages the lifetime of
92   // the identifier token.
93   Token left_identifier_token_ownership_;
94 };
95 
96 }  // namespace
97 
TEST(Operators,SourcesAppend)98 TEST(Operators, SourcesAppend) {
99   Err err;
100   TestWithScope setup;
101 
102   // Set up "sources" with an empty list.
103   const char sources[] = "sources";
104   setup.scope()->SetValue(sources, Value(nullptr, Value::LIST), nullptr);
105 
106   // Set up the operator.
107   TestBinaryOpNode node(Token::PLUS_EQUALS, "+=");
108   node.SetLeftToIdentifier(sources);
109 
110   // Append an integer.
111   node.SetRightToListOfValue(Value(nullptr, static_cast<int64_t>(5)));
112   node.Execute(setup.scope(), &err);
113   EXPECT_FALSE(err.has_error());
114 
115   // Append a string that doesn't match the pattern, it should get appended.
116   const char string1[] = "good";
117   node.SetRightToListOfValue(Value(nullptr, string1));
118   node.Execute(setup.scope(), &err);
119   EXPECT_FALSE(err.has_error());
120 
121   // Append a list with the two strings from above.
122   node.SetRightToListOfValue(Value(nullptr, string1));
123   node.Execute(setup.scope(), &err);
124   EXPECT_FALSE(err.has_error());
125 
126   // The sources variable in the scope should now have: [ 5, "good", "good" ]
127   const Value* value = setup.scope()->GetValue(sources);
128   ASSERT_TRUE(value);
129   ASSERT_EQ(Value::LIST, value->type());
130   ASSERT_EQ(3u, value->list_value().size());
131   EXPECT_TRUE(IsValueIntegerEqualing(value->list_value()[0], 5));
132   EXPECT_TRUE(IsValueStringEqualing(value->list_value()[1], "good"));
133   EXPECT_TRUE(IsValueStringEqualing(value->list_value()[2], "good"));
134 }
135 
136 // Note that the SourcesAppend test above tests the basic list + list features,
137 // this test handles the other cases.
TEST(Operators,ListAppend)138 TEST(Operators, ListAppend) {
139   Err err;
140   TestWithScope setup;
141 
142   // Set up "foo" with an empty list.
143   const char foo[] = "foo";
144   setup.scope()->SetValue(foo, Value(nullptr, Value::LIST), nullptr);
145 
146   // Set up the operator to append to "foo".
147   TestBinaryOpNode node(Token::PLUS_EQUALS, "+=");
148   node.SetLeftToIdentifier(foo);
149 
150   // Append a list with a list, the result should be a nested list.
151   Value inner_list(nullptr, Value::LIST);
152   inner_list.list_value().push_back(Value(nullptr, static_cast<int64_t>(12)));
153   node.SetRightToListOfValue(inner_list);
154 
155   Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
156                                     node.right(), &err);
157   EXPECT_FALSE(err.has_error());
158 
159   // Return from the operator should always be "none", it should update the
160   // value only.
161   EXPECT_EQ(Value::NONE, ret.type());
162 
163   // The value should be updated with "[ [ 12 ] ]"
164   Value result = *setup.scope()->GetValue(foo);
165   ASSERT_EQ(Value::LIST, result.type());
166   ASSERT_EQ(1u, result.list_value().size());
167   ASSERT_EQ(Value::LIST, result.list_value()[0].type());
168   ASSERT_EQ(1u, result.list_value()[0].list_value().size());
169   ASSERT_EQ(Value::INTEGER, result.list_value()[0].list_value()[0].type());
170   ASSERT_EQ(12, result.list_value()[0].list_value()[0].int_value());
171 
172   // Try to append an integer and a string directly (e.g. foo += "hi").
173   // This should fail.
174   const char str_str[] = "\"hi\"";
175   Token str(Location(), Token::STRING, str_str);
176   node.set_right(std::make_unique<LiteralNode>(str));
177   ExecuteBinaryOperator(setup.scope(), &node, node.left(), node.right(), &err);
178   EXPECT_TRUE(err.has_error());
179   err = Err();
180 
181   node.SetRightToValue(Value(nullptr, static_cast<int64_t>(12)));
182   ExecuteBinaryOperator(setup.scope(), &node, node.left(), node.right(), &err);
183   EXPECT_TRUE(err.has_error());
184 }
185 
TEST(Operators,ListRemove)186 TEST(Operators, ListRemove) {
187   Err err;
188   TestWithScope setup;
189 
190   const char foo_str[] = "foo";
191   const char bar_str[] = "bar";
192   Value test_list(nullptr, Value::LIST);
193   test_list.list_value().push_back(Value(nullptr, foo_str));
194   test_list.list_value().push_back(Value(nullptr, bar_str));
195   test_list.list_value().push_back(Value(nullptr, foo_str));
196 
197   // Set up "var" with an the test list.
198   const char var[] = "var";
199   setup.scope()->SetValue(var, test_list, nullptr);
200 
201   TestBinaryOpNode node(Token::MINUS_EQUALS, "-=");
202   node.SetLeftToIdentifier(var);
203 
204   // Subtract a list consisting of "foo".
205   node.SetRightToListOfValue(Value(nullptr, foo_str));
206   Value result = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
207                                        node.right(), &err);
208   EXPECT_FALSE(err.has_error());
209 
210   // -= returns an empty value to reduce the possibility of writing confusing
211   // cases like foo = bar += 1.
212   EXPECT_EQ(Value::NONE, result.type());
213 
214   // The "var" variable should have been updated. Both instances of "foo" are
215   // deleted.
216   const Value* new_value = setup.scope()->GetValue(var);
217   ASSERT_TRUE(new_value);
218   ASSERT_EQ(Value::LIST, new_value->type());
219   ASSERT_EQ(1u, new_value->list_value().size());
220   ASSERT_EQ(Value::STRING, new_value->list_value()[0].type());
221   EXPECT_EQ("bar", new_value->list_value()[0].string_value());
222 }
223 
TEST(Operators,ListSubtractWithScope)224 TEST(Operators, ListSubtractWithScope) {
225   Err err;
226   TestWithScope setup;
227 
228   Scope* scope_a = new Scope(setup.settings());
229   Value scopeval_a(nullptr, std::unique_ptr<Scope>(scope_a));
230   scope_a->SetValue("a", Value(nullptr, "foo"), nullptr);
231 
232   Scope* scope_b = new Scope(setup.settings());
233   Value scopeval_b(nullptr, std::unique_ptr<Scope>(scope_b));
234   scope_b->SetValue("b", Value(nullptr, "bar"), nullptr);
235 
236   Value lval(nullptr, Value::LIST);
237   lval.list_value().push_back(scopeval_a);
238   lval.list_value().push_back(scopeval_b);
239 
240   Scope* scope_a_other = new Scope(setup.settings());
241   Value scopeval_a_other(nullptr, std::unique_ptr<Scope>(scope_a_other));
242   scope_a_other->SetValue("a", Value(nullptr, "foo"), nullptr);
243 
244   Value rval(nullptr, Value::LIST);
245   rval.list_value().push_back(scopeval_a_other);
246 
247   TestBinaryOpNode node(Token::MINUS, "-");
248   node.SetLeftToValue(lval);
249   node.SetRightToValue(rval);
250   Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
251                                     node.right(), &err);
252   ASSERT_FALSE(err.has_error());
253   ASSERT_EQ(Value::LIST, ret.type());
254 
255   std::vector<Value> expected;
256   Scope* scope_expected = new Scope(setup.settings());
257   Value scopeval_expected(nullptr, std::unique_ptr<Scope>(scope_expected));
258   scope_expected->SetValue("b", Value(nullptr, "bar"), nullptr);
259   expected.push_back(scopeval_expected);
260   EXPECT_EQ(expected, ret.list_value());
261 }
262 
TEST(Operators,IntegerAdd)263 TEST(Operators, IntegerAdd) {
264   Err err;
265   TestWithScope setup;
266 
267   TestBinaryOpNode node(Token::PLUS, "+");
268   node.SetLeftToValue(Value(nullptr, static_cast<int64_t>(123)));
269   node.SetRightToValue(Value(nullptr, static_cast<int64_t>(456)));
270   Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
271                                     node.right(), &err);
272   ASSERT_FALSE(err.has_error());
273   ASSERT_EQ(Value::INTEGER, ret.type());
274   EXPECT_EQ(579, ret.int_value());
275 }
276 
TEST(Operators,IntegerSubtract)277 TEST(Operators, IntegerSubtract) {
278   Err err;
279   TestWithScope setup;
280 
281   TestBinaryOpNode node(Token::MINUS, "-");
282   node.SetLeftToValue(Value(nullptr, static_cast<int64_t>(123)));
283   node.SetRightToValue(Value(nullptr, static_cast<int64_t>(456)));
284   Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
285                                     node.right(), &err);
286   ASSERT_FALSE(err.has_error());
287   ASSERT_EQ(Value::INTEGER, ret.type());
288   EXPECT_EQ(-333, ret.int_value());
289 }
290 
TEST(Operators,ShortCircuitAnd)291 TEST(Operators, ShortCircuitAnd) {
292   Err err;
293   TestWithScope setup;
294 
295   // Set a && operator with the left to false.
296   TestBinaryOpNode node(Token::BOOLEAN_AND, "&&");
297   node.SetLeftToValue(Value(nullptr, false));
298 
299   // Set right as foo, but don't define a value for it.
300   const char foo[] = "foo";
301   Token identifier_token(Location(), Token::IDENTIFIER, foo);
302   node.set_right(std::make_unique<IdentifierNode>(identifier_token));
303 
304   Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
305                                     node.right(), &err);
306   EXPECT_FALSE(err.has_error());
307 }
308 
TEST(Operators,ShortCircuitOr)309 TEST(Operators, ShortCircuitOr) {
310   Err err;
311   TestWithScope setup;
312 
313   // Set a || operator with the left to true.
314   TestBinaryOpNode node(Token::BOOLEAN_OR, "||");
315   node.SetLeftToValue(Value(nullptr, true));
316 
317   // Set right as foo, but don't define a value for it.
318   const char foo[] = "foo";
319   Token identifier_token(Location(), Token::IDENTIFIER, foo);
320   node.set_right(std::make_unique<IdentifierNode>(identifier_token));
321 
322   Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
323                                     node.right(), &err);
324   EXPECT_FALSE(err.has_error());
325 }
326 
327 // Overwriting nonempty lists and scopes with other nonempty lists and scopes
328 // should be disallowed.
TEST(Operators,NonemptyOverwriting)329 TEST(Operators, NonemptyOverwriting) {
330   Err err;
331   TestWithScope setup;
332 
333   // Set up "foo" with a nonempty list.
334   const char foo[] = "foo";
335   Value old_value(nullptr, Value::LIST);
336   old_value.list_value().push_back(Value(nullptr, "string"));
337   setup.scope()->SetValue(foo, old_value, nullptr);
338 
339   TestBinaryOpNode node(Token::EQUAL, "=");
340   node.SetLeftToIdentifier(foo);
341 
342   // Assigning a nonempty list should fail.
343   node.SetRightToListOfValue(Value(nullptr, "string"));
344   node.Execute(setup.scope(), &err);
345   ASSERT_TRUE(err.has_error());
346   EXPECT_EQ("Replacing nonempty list.", err.message());
347   err = Err();
348 
349   // Assigning an empty list should succeed.
350   node.SetRightToValue(Value(nullptr, Value::LIST));
351   node.Execute(setup.scope(), &err);
352   ASSERT_FALSE(err.has_error());
353   const Value* new_value = setup.scope()->GetValue(foo);
354   ASSERT_TRUE(new_value);
355   ASSERT_EQ(Value::LIST, new_value->type());
356   ASSERT_TRUE(new_value->list_value().empty());
357 
358   // Set up "foo" with a nonempty scope.
359   const char bar[] = "bar";
360   old_value = Value(nullptr, std::make_unique<Scope>(setup.settings()));
361   old_value.scope_value()->SetValue(bar, Value(nullptr, "bar"), nullptr);
362   setup.scope()->SetValue(foo, old_value, nullptr);
363 
364   // Assigning a nonempty scope should fail (re-use old_value copy).
365   node.SetRightToValue(old_value);
366   node.Execute(setup.scope(), &err);
367   ASSERT_TRUE(err.has_error());
368   EXPECT_EQ("Replacing nonempty scope.", err.message());
369   err = Err();
370 
371   // Assigning an empty list should succeed.
372   node.SetRightToValue(
373       Value(nullptr, std::make_unique<Scope>(setup.settings())));
374   node.Execute(setup.scope(), &err);
375   ASSERT_FALSE(err.has_error());
376   new_value = setup.scope()->GetValue(foo);
377   ASSERT_TRUE(new_value);
378   ASSERT_EQ(Value::SCOPE, new_value->type());
379   ASSERT_FALSE(new_value->scope_value()->HasValues(Scope::SEARCH_CURRENT));
380 }
381 
382 // Tests this case:
383 //  foo = 1
384 //  target(...) {
385 //    foo += 1
386 //
387 // This should mark the outer "foo" as used, and the inner "foo" as unused.
TEST(Operators,PlusEqualsUsed)388 TEST(Operators, PlusEqualsUsed) {
389   Err err;
390   TestWithScope setup;
391 
392   // Outer "foo" definition, it should be unused.
393   const char foo[] = "foo";
394   Value old_value(nullptr, static_cast<int64_t>(1));
395   setup.scope()->SetValue(foo, old_value, nullptr);
396   EXPECT_TRUE(setup.scope()->IsSetButUnused(foo));
397 
398   // Nested scope.
399   Scope nested(setup.scope());
400 
401   // Run "foo += 1".
402   TestBinaryOpNode node(Token::PLUS_EQUALS, "+=");
403   node.SetLeftToIdentifier(foo);
404   node.SetRightToValue(Value(nullptr, static_cast<int64_t>(1)));
405   node.Execute(&nested, &err);
406   ASSERT_FALSE(err.has_error());
407 
408   // Outer foo should be used, inner foo should not be.
409   EXPECT_FALSE(setup.scope()->IsSetButUnused(foo));
410   EXPECT_TRUE(nested.IsSetButUnused(foo));
411 }
412