blob: 7defd242d44deb912cc5ee6372a4e872e283ad9b [file] [log] [blame]
/*
* Copyright (C) 2015, 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.
*/
#ifndef AIDL_TYPE_NAMESPACE_H_
#define AIDL_TYPE_NAMESPACE_H_
#include <memory>
#include <string>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include "aidl_language.h"
#include "logging.h"
namespace android {
namespace aidl {
// Special reserved type names.
extern const char kAidlReservedTypePackage[];
extern const char kUtf8StringClass[]; // UTF8 wire format string
extern const char kUtf8InCppStringClass[]; // UTF16 wire format, UTF8 in C++
// Helpful aliases defined to be <kAidlReservedTypePackage>.<class name>
extern const char kUtf8StringCanonicalName[];
extern const char kUtf8InCppStringCanonicalName[];
// We sometimes special case this class.
extern const char kStringCanonicalName[];
// Note that these aren't the strings recognized by the parser, we just keep
// here for the sake of logging a common string constant.
extern const char kUtf8Annotation[];
extern const char kUtf8InCppAnnotation[];
class ValidatableType {
public:
enum {
KIND_BUILT_IN,
KIND_PARCELABLE,
KIND_INTERFACE,
KIND_GENERATED,
};
ValidatableType(int kind,
const std::string& package, const std::string& type_name,
const std::string& decl_file, int decl_line);
virtual ~ValidatableType() = default;
virtual bool CanBeArray() const { return ArrayType() != nullptr; }
virtual bool CanBeOutParameter() const = 0;
virtual bool CanWriteToParcel() const = 0;
virtual const ValidatableType* ArrayType() const = 0;
virtual const ValidatableType* NullableType() const = 0;
// ShortName() is the class name without a package.
std::string ShortName() const { return type_name_; }
// CanonicalName() returns the canonical AIDL type, with packages.
std::string CanonicalName() const { return canonical_name_; }
int Kind() const { return kind_; }
std::string HumanReadableKind() const;
std::string DeclFile() const { return origin_file_; }
int DeclLine() const { return origin_line_; }
private:
const int kind_;
const std::string type_name_;
const std::string canonical_name_;
const std::string origin_file_;
const int origin_line_;
DISALLOW_COPY_AND_ASSIGN(ValidatableType);
};
class TypeNamespace {
public:
// Load the TypeNamespace with built in types. Don't do work in the
// constructor because many of the useful methods are virtual.
virtual void Init() = 0;
// Load this TypeNamespace with user defined types.
virtual bool AddParcelableType(const AidlParcelable& p,
const std::string& filename) = 0;
virtual bool AddBinderType(const AidlInterface& b,
const std::string& filename) = 0;
// Add a container type to this namespace. Returns false only
// on error. Silently discards requests to add non-container types.
virtual bool MaybeAddContainerType(const AidlType& aidl_type) = 0;
// Returns true iff this has a type for |import|.
virtual bool HasImportType(const AidlImport& import) const = 0;
// Returns true iff |package| is a valid package name.
virtual bool IsValidPackage(const std::string& package) const;
// Returns a pointer to a type corresponding to |raw_type| or nullptr
// if this is an invalid return type.
virtual const ValidatableType* GetReturnType(
const AidlType& raw_type,
const std::string& filename,
const AidlInterface& interface) const;
// Returns a pointer to a type corresponding to |a| or nullptr if |a|
// has an invalid argument type.
virtual const ValidatableType* GetArgType(
const AidlArgument& a,
int arg_index,
const std::string& filename,
const AidlInterface& interface) const;
// Returns a pointer to a type corresponding to |interface|.
virtual const ValidatableType* GetInterfaceType(
const AidlInterface& interface) const = 0;
protected:
TypeNamespace() = default;
virtual ~TypeNamespace() = default;
virtual const ValidatableType* GetValidatableType(
const AidlType& type, std::string* error_msg,
const AidlInterface& interface) const = 0;
private:
DISALLOW_COPY_AND_ASSIGN(TypeNamespace);
};
template<typename T>
class LanguageTypeNamespace : public TypeNamespace {
public:
LanguageTypeNamespace() = default;
virtual ~LanguageTypeNamespace() = default;
// Get a pointer to an existing type. Searches first by fully-qualified
// name, and then class name (dropping package qualifiers).
const T* Find(const AidlType& aidl_type) const;
// Find a type by its |name|. If |name| refers to a container type (e.g.
// List<String>) you must turn it into a canonical name first (e.g.
// java.util.List<java.lang.String>).
const T* FindTypeByCanonicalName(const std::string& name) const;
bool HasTypeByCanonicalName(const std::string& type_name) const {
return FindTypeByCanonicalName(type_name) != nullptr;
}
bool HasImportType(const AidlImport& import) const override {
return HasTypeByCanonicalName(import.GetNeededClass());
}
const ValidatableType* GetInterfaceType(
const AidlInterface& interface) const override {
return FindTypeByCanonicalName(interface.GetCanonicalName());
}
bool MaybeAddContainerType(const AidlType& aidl_type) override;
// We dynamically create container types as we discover them in the parse
// tree. Returns false if the contained types cannot be canonicalized.
virtual bool AddListType(const std::string& contained_type_name) = 0;
virtual bool AddMapType(const std::string& key_type_name,
const std::string& value_type_name) = 0;
protected:
bool Add(const T* type);
private:
// Returns true iff the name can be canonicalized to a container type.
virtual bool CanonicalizeContainerType(
const AidlType& aidl_type,
std::vector<std::string>* container_class,
std::vector<std::string>* contained_type_names) const;
// Returns true if this is a container type, rather than a normal type.
bool IsContainerType(const std::string& type_name) const;
const ValidatableType* GetValidatableType(
const AidlType& type, std::string* error_msg,
const AidlInterface& interface) const override;
std::vector<std::unique_ptr<const T>> types_;
DISALLOW_COPY_AND_ASSIGN(LanguageTypeNamespace);
}; // class LanguageTypeNamespace
template<typename T>
bool LanguageTypeNamespace<T>::Add(const T* type) {
const T* existing = FindTypeByCanonicalName(type->CanonicalName());
if (!existing) {
types_.emplace_back(type);
return true;
}
if (existing->Kind() == ValidatableType::KIND_BUILT_IN) {
LOG(ERROR) << type->DeclFile() << ":" << type->DeclLine()
<< " attempt to redefine built in class "
<< type->CanonicalName();
return false;
}
if (type->Kind() != existing->Kind()) {
LOG(ERROR) << type->DeclFile() << ":" << type->DeclLine()
<< " attempt to redefine " << type->CanonicalName()
<< " as " << type->HumanReadableKind();
LOG(ERROR) << existing->DeclFile() << ":" << existing->DeclLine()
<< " previously defined here as "
<< existing->HumanReadableKind();
return false;
}
return true;
}
template<typename T>
const T* LanguageTypeNamespace<T>::Find(const AidlType& aidl_type) const {
using std::string;
using std::vector;
using android::base::Join;
using android::base::Trim;
string name = Trim(aidl_type.GetName());
if (IsContainerType(name)) {
vector<string> container_class;
vector<string> contained_type_names;
if (!CanonicalizeContainerType(aidl_type, &container_class,
&contained_type_names)) {
return nullptr;
}
name = Join(container_class, '.') +
"<" + Join(contained_type_names, ',') + ">";
}
// Here, we know that we have the canonical name for this container.
return FindTypeByCanonicalName(name);
}
template<typename T>
const T* LanguageTypeNamespace<T>::FindTypeByCanonicalName(
const std::string& raw_name) const {
using android::base::Trim;
std::string name = Trim(raw_name);
const T* ret = nullptr;
for (const auto& type : types_) {
// Always prefer a exact match if possible.
// This works for primitives and class names qualified with a package.
if (type->CanonicalName() == name) {
ret = type.get();
break;
}
// We allow authors to drop packages when refering to a class name.
if (type->ShortName() == name) {
ret = type.get();
}
}
return ret;
}
template<typename T>
bool LanguageTypeNamespace<T>::MaybeAddContainerType(
const AidlType& aidl_type) {
using android::base::Join;
const std::string& type_name = aidl_type.GetName();
if (!IsContainerType(type_name)) {
return true;
}
std::vector<std::string> container_class;
std::vector<std::string> contained_type_names;
if (!CanonicalizeContainerType(aidl_type, &container_class,
&contained_type_names)) {
return false;
}
const std::string canonical_name = Join(container_class, ".") +
"<" + Join(contained_type_names, ",") + ">";
if (HasTypeByCanonicalName(canonical_name)) {
return true;
}
// We only support two types right now and this type is one of them.
switch (contained_type_names.size()) {
case 1:
return AddListType(contained_type_names[0]);
case 2:
return AddMapType(contained_type_names[0], contained_type_names[1]);
default:
break; // Should never get here, will FATAL below.
}
LOG(FATAL) << "aidl internal error";
return false;
}
template<typename T>
bool LanguageTypeNamespace<T>::IsContainerType(
const std::string& type_name) const {
const size_t opening_brace = type_name.find('<');
const size_t closing_brace = type_name.find('>');
if (opening_brace != std::string::npos ||
closing_brace != std::string::npos) {
return true; // Neither < nor > appear in normal AIDL types.
}
return false;
}
template<typename T>
bool LanguageTypeNamespace<T>::CanonicalizeContainerType(
const AidlType& aidl_type,
std::vector<std::string>* container_class,
std::vector<std::string>* contained_type_names) const {
using android::base::Trim;
using android::base::Split;
std::string name = Trim(aidl_type.GetName());
const size_t opening_brace = name.find('<');
const size_t closing_brace = name.find('>');
if (opening_brace == std::string::npos ||
closing_brace == std::string::npos) {
return false;
}
if (opening_brace != name.rfind('<') ||
closing_brace != name.rfind('>') ||
closing_brace != name.length() - 1) {
// Nested/invalid templates are forbidden.
LOG(ERROR) << "Invalid template type '" << name << "'";
return false;
}
std::string container = Trim(name.substr(0, opening_brace));
std::string remainder = name.substr(opening_brace + 1,
(closing_brace - opening_brace) - 1);
std::vector<std::string> args = Split(remainder, ",");
for (auto& type_name: args) {
// Here, we are relying on FindTypeByCanonicalName to do its best when
// given a non-canonical name for non-compound type (i.e. not another
// container).
const T* arg_type = FindTypeByCanonicalName(type_name);
if (!arg_type) {
return false;
}
// Now get the canonical names for these contained types, remapping them if
// necessary.
type_name = arg_type->CanonicalName();
if (aidl_type.IsUtf8() && type_name == "java.lang.String") {
type_name = kUtf8StringCanonicalName;
} else if (aidl_type.IsUtf8InCpp() && type_name == "java.lang.String") {
type_name = kUtf8InCppStringCanonicalName;
}
}
// Map the container name to its canonical form for supported containers.
if ((container == "List" || container == "java.util.List") &&
args.size() == 1) {
*container_class = {"java", "util", "List"};
*contained_type_names = args;
return true;
}
if ((container == "Map" || container == "java.util.Map") &&
args.size() == 2) {
*container_class = {"java", "util", "Map"};
*contained_type_names = args;
return true;
}
LOG(ERROR) << "Unknown find container with name " << container
<< " and " << args.size() << "contained types.";
return false;
}
template<typename T>
const ValidatableType* LanguageTypeNamespace<T>::GetValidatableType(
const AidlType& aidl_type, std::string* error_msg,
const AidlInterface& interface) const {
using android::base::StringPrintf;
const ValidatableType* type = Find(aidl_type);
if (type == nullptr) {
*error_msg = "unknown type";
return nullptr;
}
if (aidl_type.GetName() == "void") {
if (aidl_type.IsArray()) {
*error_msg = "void type cannot be an array";
return nullptr;
}
if (aidl_type.IsNullable() || aidl_type.IsUtf8() ||
aidl_type.IsUtf8InCpp()) {
*error_msg = "void type cannot be annotated";
return nullptr;
}
// We have no more special handling for void.
return type;
}
// No type may be annotated with both these annotations.
if (aidl_type.IsUtf8() && aidl_type.IsUtf8InCpp()) {
*error_msg = StringPrintf("Type cannot be marked as both %s and %s.",
kUtf8Annotation, kUtf8InCppAnnotation);
return nullptr;
}
bool utf8 = aidl_type.IsUtf8();
bool utf8InCpp = aidl_type.IsUtf8InCpp();
// Strings inside containers get remapped to appropriate utf8 versions when
// we convert the container name to its canonical form and the look up the
// type. However, for non-compound types (i.e. those not in a container) we
// must patch them up here.
if (IsContainerType(type->CanonicalName())) {
utf8 = false;
utf8InCpp = false;
} else if (aidl_type.GetName() == "String" ||
aidl_type.GetName() == "java.lang.String") {
utf8 = utf8 || interface.IsUtf8();
utf8InCpp = utf8InCpp || interface.IsUtf8InCpp();
} else if (utf8 || utf8InCpp) {
const char* annotation_literal =
(utf8) ? kUtf8Annotation : kUtf8InCppAnnotation;
*error_msg = StringPrintf("type '%s' may not be annotated as %s.",
aidl_type.GetName().c_str(),
annotation_literal);
return nullptr;
}
if (utf8) {
type = FindTypeByCanonicalName(kUtf8StringCanonicalName);
} else if (utf8InCpp) {
type = FindTypeByCanonicalName(kUtf8InCppStringCanonicalName);
}
// One of our UTF8 transforms made type null
if (type == nullptr) {
const char* annotation_literal =
(utf8) ? kUtf8Annotation : kUtf8InCppAnnotation;
*error_msg = StringPrintf(
"%s is unsupported when generating code for this language.",
annotation_literal);
return nullptr;
}
if (!type->CanWriteToParcel()) {
*error_msg = "type cannot be marshalled";
return nullptr;
}
if (aidl_type.IsArray()) {
type = type->ArrayType();
if (!type) {
*error_msg = StringPrintf("type '%s' cannot be an array",
aidl_type.GetName().c_str());
return nullptr;
}
}
if (interface.IsNullable()) {
const ValidatableType* nullableType = type->NullableType();
if (nullableType) {
return nullableType;
}
}
if (aidl_type.IsNullable()) {
type = type->NullableType();
if (!type) {
*error_msg = StringPrintf("type '%s%s' cannot be marked as possibly null",
aidl_type.GetName().c_str(),
(aidl_type.IsArray()) ? "[]" : "");
return nullptr;
}
}
return type;
}
} // namespace aidl
} // namespace android
#endif // AIDL_TYPE_NAMESPACE_H_