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/value.h"
6 
7 #include <stddef.h>
8 #include <utility>
9 
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "tools/gn/scope.h"
13 
14 // NOTE: Cannot use = default here due to the use of a union member.
Value()15 Value::Value() {}
16 
Value(const ParseNode * origin,Type t)17 Value::Value(const ParseNode* origin, Type t) : type_(t), origin_(origin) {
18   switch (type_) {
19     case NONE:
20       break;
21     case BOOLEAN:
22       boolean_value_ = false;
23       break;
24     case INTEGER:
25       int_value_ = 0;
26       break;
27     case STRING:
28       new (&string_value_) std::string();
29       break;
30     case LIST:
31       new (&list_value_) std::vector<Value>();
32       break;
33     case SCOPE:
34       new (&scope_value_) std::unique_ptr<Scope>();
35       break;
36   }
37 }
38 
Value(const ParseNode * origin,bool bool_val)39 Value::Value(const ParseNode* origin, bool bool_val)
40     : type_(BOOLEAN),
41       boolean_value_(bool_val),
42       origin_(origin) {}
43 
Value(const ParseNode * origin,int64_t int_val)44 Value::Value(const ParseNode* origin, int64_t int_val)
45     : type_(INTEGER),
46       int_value_(int_val),
47       origin_(origin) {}
48 
Value(const ParseNode * origin,std::string str_val)49 Value::Value(const ParseNode* origin, std::string str_val)
50     : type_(STRING),
51       string_value_(std::move(str_val)),
52       origin_(origin) {}
53 
Value(const ParseNode * origin,const char * str_val)54 Value::Value(const ParseNode* origin, const char* str_val)
55     : type_(STRING),
56       string_value_(str_val),
57       origin_(origin) {}
58 
Value(const ParseNode * origin,std::unique_ptr<Scope> scope)59 Value::Value(const ParseNode* origin, std::unique_ptr<Scope> scope)
60     : type_(SCOPE),
61       scope_value_(std::move(scope)),
62       origin_(origin) {}
63 
Value(const Value & other)64 Value::Value(const Value& other) : type_(other.type_), origin_(other.origin_) {
65   switch (type_) {
66     case NONE:
67       break;
68     case BOOLEAN:
69       boolean_value_ = other.boolean_value_;
70       break;
71     case INTEGER:
72       int_value_ = other.int_value_;
73       break;
74     case STRING:
75       new (&string_value_) std::string(other.string_value_);
76       break;
77     case LIST:
78       new (&list_value_) std::vector<Value>(other.list_value_);
79       break;
80     case SCOPE:
81       new (&scope_value_) std::unique_ptr<Scope>(
82           other.scope_value_.get() ? other.scope_value_->MakeClosure()
83                                    : nullptr);
84       break;
85   }
86 }
87 
Value(Value && other)88 Value::Value(Value&& other) noexcept
89     : type_(other.type_), origin_(other.origin_) {
90   switch (type_) {
91     case NONE:
92       break;
93     case BOOLEAN:
94       boolean_value_ = other.boolean_value_;
95       break;
96     case INTEGER:
97       int_value_ = other.int_value_;
98       break;
99     case STRING:
100       new (&string_value_) std::string(std::move(other.string_value_));
101       break;
102     case LIST:
103       new (&list_value_) std::vector<Value>(std::move(other.list_value_));
104       break;
105     case SCOPE:
106       new (&scope_value_) std::unique_ptr<Scope>(std::move(other.scope_value_));
107       break;
108   }
109 }
110 
operator =(const Value & other)111 Value& Value::operator=(const Value& other) {
112   if (this != &other) {
113     this->~Value();
114     new (this) Value(other);
115   }
116   return *this;
117 }
118 
operator =(Value && other)119 Value& Value::operator=(Value&& other) noexcept {
120   if (this != &other) {
121     this->~Value();
122     new (this) Value(std::move(other));
123   }
124   return *this;
125 }
126 
~Value()127 Value::~Value() {
128   using namespace std;
129   switch (type_) {
130     case STRING:
131       string_value_.~string();
132       break;
133     case LIST:
134       list_value_.~vector<Value>();
135       break;
136     case SCOPE:
137       scope_value_.~unique_ptr<Scope>();
138       break;
139     default:;
140   }
141 }
142 
143 // static
DescribeType(Type t)144 const char* Value::DescribeType(Type t) {
145   switch (t) {
146     case NONE:
147       return "none";
148     case BOOLEAN:
149       return "boolean";
150     case INTEGER:
151       return "integer";
152     case STRING:
153       return "string";
154     case LIST:
155       return "list";
156     case SCOPE:
157       return "scope";
158     default:
159       NOTREACHED();
160       return "UNKNOWN";
161   }
162 }
163 
SetScopeValue(std::unique_ptr<Scope> scope)164 void Value::SetScopeValue(std::unique_ptr<Scope> scope) {
165   DCHECK(type_ == SCOPE);
166   scope_value_ = std::move(scope);
167 }
168 
ToString(bool quote_string) const169 std::string Value::ToString(bool quote_string) const {
170   switch (type_) {
171     case NONE:
172       return "<void>";
173     case BOOLEAN:
174       return boolean_value_ ? "true" : "false";
175     case INTEGER:
176       return base::Int64ToString(int_value_);
177     case STRING:
178       if (quote_string) {
179         std::string result = "\"";
180         bool hanging_backslash = false;
181         for (char ch : string_value_) {
182           // If the last character was a literal backslash and the next
183           // character could form a valid escape sequence, we need to insert
184           // an extra backslash to prevent that.
185           if (hanging_backslash && (ch == '$' || ch == '"' || ch == '\\'))
186             result += '\\';
187           // If the next character is a dollar sign or double quote, it needs
188           // to be escaped; otherwise it can be printed as is.
189           if (ch == '$' || ch == '"')
190             result += '\\';
191           result += ch;
192           hanging_backslash = (ch == '\\');
193         }
194         // Again, we need to prevent the closing double quotes from becoming
195         // an escape sequence.
196         if (hanging_backslash)
197           result += '\\';
198         result += '"';
199         return result;
200       }
201       return string_value_;
202     case LIST: {
203       std::string result = "[";
204       for (size_t i = 0; i < list_value_.size(); i++) {
205         if (i > 0)
206           result += ", ";
207         result += list_value_[i].ToString(true);
208       }
209       result.push_back(']');
210       return result;
211     }
212     case SCOPE: {
213       Scope::KeyValueMap scope_values;
214       scope_value_->GetCurrentScopeValues(&scope_values);
215       if (scope_values.empty())
216         return std::string("{ }");
217 
218       std::string result = "{\n";
219       for (const auto& pair : scope_values) {
220         result += "  " + pair.first.as_string() + " = " +
221                   pair.second.ToString(true) + "\n";
222       }
223       result += "}";
224 
225       return result;
226     }
227   }
228   return std::string();
229 }
230 
VerifyTypeIs(Type t,Err * err) const231 bool Value::VerifyTypeIs(Type t, Err* err) const {
232   if (type_ == t)
233     return true;
234 
235   *err = Err(origin(), std::string("This is not a ") + DescribeType(t) + ".",
236              std::string("Instead I see a ") + DescribeType(type_) + " = " +
237                  ToString(true));
238   return false;
239 }
240 
operator ==(const Value & other) const241 bool Value::operator==(const Value& other) const {
242   if (type_ != other.type_)
243     return false;
244 
245   switch (type_) {
246     case Value::BOOLEAN:
247       return boolean_value() == other.boolean_value();
248     case Value::INTEGER:
249       return int_value() == other.int_value();
250     case Value::STRING:
251       return string_value() == other.string_value();
252     case Value::LIST:
253       if (list_value().size() != other.list_value().size())
254         return false;
255       for (size_t i = 0; i < list_value().size(); i++) {
256         if (list_value()[i] != other.list_value()[i])
257           return false;
258       }
259       return true;
260     case Value::SCOPE:
261       return scope_value()->CheckCurrentScopeValuesEqual(other.scope_value());
262     case Value::NONE:
263       return false;
264     default:
265       NOTREACHED();
266       return false;
267   }
268 }
269 
operator !=(const Value & other) const270 bool Value::operator!=(const Value& other) const {
271   return !operator==(other);
272 }
273