|  | // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "tools/gn/value.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/strings/string_util.h" | 
|  | #include "tools/gn/scope.h" | 
|  |  | 
|  | // NOTE: Cannot use = default here due to the use of a union member. | 
|  | Value::Value() {} | 
|  |  | 
|  | Value::Value(const ParseNode* origin, Type t) : type_(t), origin_(origin) { | 
|  | switch (type_) { | 
|  | case NONE: | 
|  | break; | 
|  | case BOOLEAN: | 
|  | boolean_value_ = false; | 
|  | break; | 
|  | case INTEGER: | 
|  | int_value_ = 0; | 
|  | break; | 
|  | case STRING: | 
|  | new (&string_value_) std::string(); | 
|  | break; | 
|  | case LIST: | 
|  | new (&list_value_) std::vector<Value>(); | 
|  | break; | 
|  | case SCOPE: | 
|  | new (&scope_value_) std::unique_ptr<Scope>(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | Value::Value(const ParseNode* origin, bool bool_val) | 
|  | : type_(BOOLEAN), boolean_value_(bool_val), origin_(origin) {} | 
|  |  | 
|  | Value::Value(const ParseNode* origin, int64_t int_val) | 
|  | : type_(INTEGER), int_value_(int_val), origin_(origin) {} | 
|  |  | 
|  | Value::Value(const ParseNode* origin, std::string str_val) | 
|  | : type_(STRING), string_value_(std::move(str_val)), origin_(origin) {} | 
|  |  | 
|  | Value::Value(const ParseNode* origin, const char* str_val) | 
|  | : type_(STRING), string_value_(str_val), origin_(origin) {} | 
|  |  | 
|  | Value::Value(const ParseNode* origin, std::unique_ptr<Scope> scope) | 
|  | : type_(SCOPE), scope_value_(std::move(scope)), origin_(origin) {} | 
|  |  | 
|  | Value::Value(const Value& other) : type_(other.type_), origin_(other.origin_) { | 
|  | switch (type_) { | 
|  | case NONE: | 
|  | break; | 
|  | case BOOLEAN: | 
|  | boolean_value_ = other.boolean_value_; | 
|  | break; | 
|  | case INTEGER: | 
|  | int_value_ = other.int_value_; | 
|  | break; | 
|  | case STRING: | 
|  | new (&string_value_) std::string(other.string_value_); | 
|  | break; | 
|  | case LIST: | 
|  | new (&list_value_) std::vector<Value>(other.list_value_); | 
|  | break; | 
|  | case SCOPE: | 
|  | new (&scope_value_) std::unique_ptr<Scope>( | 
|  | other.scope_value_.get() ? other.scope_value_->MakeClosure() | 
|  | : nullptr); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | Value::Value(Value&& other) noexcept | 
|  | : type_(other.type_), origin_(other.origin_) { | 
|  | switch (type_) { | 
|  | case NONE: | 
|  | break; | 
|  | case BOOLEAN: | 
|  | boolean_value_ = other.boolean_value_; | 
|  | break; | 
|  | case INTEGER: | 
|  | int_value_ = other.int_value_; | 
|  | break; | 
|  | case STRING: | 
|  | new (&string_value_) std::string(std::move(other.string_value_)); | 
|  | break; | 
|  | case LIST: | 
|  | new (&list_value_) std::vector<Value>(std::move(other.list_value_)); | 
|  | break; | 
|  | case SCOPE: | 
|  | new (&scope_value_) std::unique_ptr<Scope>(std::move(other.scope_value_)); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | Value& Value::operator=(const Value& other) { | 
|  | if (this != &other) { | 
|  | this->~Value(); | 
|  | new (this) Value(other); | 
|  | } | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | Value& Value::operator=(Value&& other) noexcept { | 
|  | if (this != &other) { | 
|  | this->~Value(); | 
|  | new (this) Value(std::move(other)); | 
|  | } | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | Value::~Value() { | 
|  | using namespace std; | 
|  | switch (type_) { | 
|  | case STRING: | 
|  | string_value_.~string(); | 
|  | break; | 
|  | case LIST: | 
|  | list_value_.~vector<Value>(); | 
|  | break; | 
|  | case SCOPE: | 
|  | scope_value_.~unique_ptr<Scope>(); | 
|  | break; | 
|  | default:; | 
|  | } | 
|  | } | 
|  |  | 
|  | // static | 
|  | const char* Value::DescribeType(Type t) { | 
|  | switch (t) { | 
|  | case NONE: | 
|  | return "none"; | 
|  | case BOOLEAN: | 
|  | return "boolean"; | 
|  | case INTEGER: | 
|  | return "integer"; | 
|  | case STRING: | 
|  | return "string"; | 
|  | case LIST: | 
|  | return "list"; | 
|  | case SCOPE: | 
|  | return "scope"; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | return "UNKNOWN"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Value::SetScopeValue(std::unique_ptr<Scope> scope) { | 
|  | DCHECK(type_ == SCOPE); | 
|  | scope_value_ = std::move(scope); | 
|  | } | 
|  |  | 
|  | std::string Value::ToString(bool quote_string) const { | 
|  | switch (type_) { | 
|  | case NONE: | 
|  | return "<void>"; | 
|  | case BOOLEAN: | 
|  | return boolean_value_ ? "true" : "false"; | 
|  | case INTEGER: | 
|  | return base::Int64ToString(int_value_); | 
|  | case STRING: | 
|  | if (quote_string) { | 
|  | std::string result = "\""; | 
|  | bool hanging_backslash = false; | 
|  | for (char ch : string_value_) { | 
|  | // If the last character was a literal backslash and the next | 
|  | // character could form a valid escape sequence, we need to insert | 
|  | // an extra backslash to prevent that. | 
|  | if (hanging_backslash && (ch == '$' || ch == '"' || ch == '\\')) | 
|  | result += '\\'; | 
|  | // If the next character is a dollar sign or double quote, it needs | 
|  | // to be escaped; otherwise it can be printed as is. | 
|  | if (ch == '$' || ch == '"') | 
|  | result += '\\'; | 
|  | result += ch; | 
|  | hanging_backslash = (ch == '\\'); | 
|  | } | 
|  | // Again, we need to prevent the closing double quotes from becoming | 
|  | // an escape sequence. | 
|  | if (hanging_backslash) | 
|  | result += '\\'; | 
|  | result += '"'; | 
|  | return result; | 
|  | } | 
|  | return string_value_; | 
|  | case LIST: { | 
|  | std::string result = "["; | 
|  | for (size_t i = 0; i < list_value_.size(); i++) { | 
|  | if (i > 0) | 
|  | result += ", "; | 
|  | result += list_value_[i].ToString(true); | 
|  | } | 
|  | result.push_back(']'); | 
|  | return result; | 
|  | } | 
|  | case SCOPE: { | 
|  | Scope::KeyValueMap scope_values; | 
|  | scope_value_->GetCurrentScopeValues(&scope_values); | 
|  | if (scope_values.empty()) | 
|  | return std::string("{ }"); | 
|  |  | 
|  | std::string result = "{\n"; | 
|  | for (const auto& pair : scope_values) { | 
|  | result += "  " + std::string(pair.first) + " = " + | 
|  | pair.second.ToString(true) + "\n"; | 
|  | } | 
|  | result += "}"; | 
|  |  | 
|  | return result; | 
|  | } | 
|  | } | 
|  | return std::string(); | 
|  | } | 
|  |  | 
|  | bool Value::VerifyTypeIs(Type t, Err* err) const { | 
|  | if (type_ == t) | 
|  | return true; | 
|  |  | 
|  | *err = Err(origin(), std::string("This is not a ") + DescribeType(t) + ".", | 
|  | std::string("Instead I see a ") + DescribeType(type_) + " = " + | 
|  | ToString(true)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool Value::operator==(const Value& other) const { | 
|  | if (type_ != other.type_) | 
|  | return false; | 
|  |  | 
|  | switch (type_) { | 
|  | case Value::BOOLEAN: | 
|  | return boolean_value() == other.boolean_value(); | 
|  | case Value::INTEGER: | 
|  | return int_value() == other.int_value(); | 
|  | case Value::STRING: | 
|  | return string_value() == other.string_value(); | 
|  | case Value::LIST: | 
|  | if (list_value().size() != other.list_value().size()) | 
|  | return false; | 
|  | for (size_t i = 0; i < list_value().size(); i++) { | 
|  | if (list_value()[i] != other.list_value()[i]) | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | case Value::SCOPE: | 
|  | return scope_value()->CheckCurrentScopeValuesEqual(other.scope_value()); | 
|  | case Value::NONE: | 
|  | return false; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool Value::operator!=(const Value& other) const { | 
|  | return !operator==(other); | 
|  | } |