blob: a54db03030a7d801ff2f7dcb4cab19c60160c03f [file] [log] [blame]
// 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 += " " + pair.first.as_string() + " = " +
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);
}