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