blob: abbef6a3bd33439c58e3e5b353453bce52473675 [file] [log] [blame]
/*
* Copyright (C) 2019, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "aidl.h"
#include "aidl_language.h"
#include "logging.h"
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <memory>
#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
using android::base::Join;
using std::string;
using std::unique_ptr;
using std::vector;
static bool isValidLiteralChar(char c) {
return !(c <= 0x1f || // control characters are < 0x20
c >= 0x7f || // DEL is 0x7f
c == '\\'); // Disallow backslashes for future proofing.
}
AidlConstantValue* AidlConstantValue::Boolean(const AidlLocation& location, bool value) {
return new AidlConstantValue(location, Type::BOOLEAN, value ? "true" : "false");
}
AidlConstantValue* AidlConstantValue::Character(const AidlLocation& location, char value) {
if (!isValidLiteralChar(value)) {
AIDL_ERROR(location) << "Invalid character literal " << value;
return new AidlConstantValue(location, Type::ERROR, "");
}
return new AidlConstantValue(location, Type::CHARACTER, std::string("'") + value + "'");
}
AidlConstantValue* AidlConstantValue::Floating(const AidlLocation& location,
const std::string& value) {
return new AidlConstantValue(location, Type::FLOATING, value);
}
AidlConstantValue* AidlConstantValue::Hex(const AidlLocation& location, const std::string& value) {
return new AidlConstantValue(location, Type::HEXIDECIMAL, value);
}
AidlConstantValue* AidlConstantValue::Integral(const AidlLocation& location,
const std::string& value) {
return new AidlConstantValue(location, Type::INTEGRAL, value);
}
AidlConstantValue* AidlConstantValue::Array(
const AidlLocation& location, std::vector<std::unique_ptr<AidlConstantValue>>* values) {
return new AidlConstantValue(location, Type::ARRAY, values);
}
AidlConstantValue* AidlConstantValue::String(const AidlLocation& location,
const std::string& value) {
for (size_t i = 0; i < value.length(); ++i) {
if (!isValidLiteralChar(value[i])) {
AIDL_ERROR(location) << "Found invalid character at index " << i << " in string constant '"
<< value << "'";
return new AidlConstantValue(location, Type::ERROR, "");
}
}
return new AidlConstantValue(location, Type::STRING, value);
}
bool AidlConstantValue::CheckValid() const {
// error always logged during creation
return type_ != AidlConstantValue::Type::ERROR;
}
static string TrimIfSuffix(const string& str, const string& suffix) {
if (str.size() > suffix.size() &&
0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix)) {
return str.substr(0, str.size() - suffix.size());
}
return str;
}
string AidlConstantValue::As(const AidlTypeSpecifier& type,
const ConstantValueDecorator& decorator) const {
if (type.IsGeneric()) {
AIDL_ERROR(type) << "Generic type cannot be specified with a constant literal.";
return "";
}
const std::string& type_string = type.GetName();
if ((type_ == Type::ARRAY) != type.IsArray()) {
goto mismatch_error;
}
switch (type_) {
case AidlConstantValue::Type::ARRAY: {
vector<string> raw_values;
raw_values.reserve(values_.size());
bool success = true;
for (const auto& value : values_) {
const AidlTypeSpecifier& array_base = type.ArrayBase();
const std::string raw_value = value->As(array_base, decorator);
success &= !raw_value.empty();
raw_values.push_back(decorator(array_base, raw_value));
}
if (!success) {
AIDL_ERROR(this) << "Default value must be a literal array of " << type_string << ".";
return "";
}
return decorator(type, "{" + Join(raw_values, ", ") + "}");
}
case AidlConstantValue::Type::BOOLEAN:
if (type_string == "boolean") return decorator(type, value_);
goto mismatch_error;
case AidlConstantValue::Type::CHARACTER:
if (type_string == "char") return decorator(type, value_);
goto mismatch_error;
case AidlConstantValue::Type::FLOATING: {
bool is_float_literal = value_.back() == 'f';
const std::string raw_value = TrimIfSuffix(value_, "f");
if (type_string == "double") {
double parsed_value;
if (!android::base::ParseDouble(raw_value, &parsed_value)) goto parse_error;
return decorator(type, std::to_string(parsed_value));
}
if (is_float_literal && type_string == "float") {
float parsed_value;
if (!android::base::ParseFloat(raw_value, &parsed_value)) goto parse_error;
return decorator(type, std::to_string(parsed_value) + "f");
}
goto mismatch_error;
}
case AidlConstantValue::Type::HEXIDECIMAL:
// For historical reasons, a hexidecimal int needs to have the specified bits interpreted
// as the signed type, so the other types are made consistent with it.
if (type_string == "byte") {
uint8_t unsigned_value;
if (!android::base::ParseUint<uint8_t>(value_, &unsigned_value)) goto parse_error;
return decorator(type, std::to_string((int8_t)unsigned_value));
}
if (type_string == "int") {
uint32_t unsigned_value;
if (!android::base::ParseUint<uint32_t>(value_, &unsigned_value)) goto parse_error;
return decorator(type, std::to_string((int32_t)unsigned_value));
}
if (type_string == "long") {
uint64_t unsigned_value;
if (!android::base::ParseUint<uint64_t>(value_, &unsigned_value)) goto parse_error;
return decorator(type, std::to_string((int64_t)unsigned_value));
}
goto mismatch_error;
case AidlConstantValue::Type::INTEGRAL:
if (type_string == "byte") {
if (!android::base::ParseInt<int8_t>(value_, nullptr)) goto parse_error;
return decorator(type, value_);
}
if (type_string == "int") {
if (!android::base::ParseInt<int32_t>(value_, nullptr)) goto parse_error;
return decorator(type, value_);
}
if (type_string == "long") {
if (!android::base::ParseInt<int64_t>(value_, nullptr)) goto parse_error;
return decorator(type, value_);
}
goto mismatch_error;
case AidlConstantValue::Type::STRING:
if (type_string == "String") return decorator(type, value_);
goto mismatch_error;
default:
AIDL_FATAL(this) << "Unrecognized constant value type";
}
mismatch_error:
AIDL_ERROR(this) << "Expecting type " << type_string << " but constant is " << ToString(type_);
return "";
parse_error:
AIDL_ERROR(this) << "Could not parse " << value_ << " as " << type_string;
return "";
}
string AidlConstantValue::ToString(Type type) {
switch (type) {
case Type::ARRAY:
return "a literal array";
case Type::BOOLEAN:
return "a literal boolean";
case Type::CHARACTER:
return "a literal char";
case Type::FLOATING:
return "a floating-point literal";
case Type::HEXIDECIMAL:
return "a hexidecimal literal";
case Type::INTEGRAL:
return "an integral literal";
case Type::STRING:
return "a literal string";
case Type::ERROR:
LOG(FATAL) << "aidl internal error: error type failed to halt program";
return "";
default:
LOG(FATAL) << "aidl internal error: unknown constant type: " << static_cast<int>(type);
return ""; // not reached
}
}
AidlConstantValue::AidlConstantValue(const AidlLocation& location, Type type,
std::vector<std::unique_ptr<AidlConstantValue>>* values)
: AidlNode(location), type_(type), values_(std::move(*values)) {}
AidlConstantValue::AidlConstantValue(const AidlLocation& location, Type type,
const std::string& checked_value)
: AidlNode(location), type_(type), value_(checked_value) {
CHECK(!value_.empty() || type_ == Type::ERROR);
CHECK(type_ != Type::ARRAY);
}