blob: 7aaba7b0bb2e3386ab4811903e472f6d0c007e60 [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 <base/macros.h>
#include "aidl_language.h"
#include "logging.h"
namespace android {
namespace aidl {
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 = 0;
virtual bool CanBeOutParameter() const = 0;
virtual bool CanWriteToParcel() const = 0;
// Name() returns the short name of an object (without package qualifiers).
virtual std::string Name() const { return type_name_; }
// QualifiedName() returns the canonical AIDL type, with packages.
virtual std::string QualifiedName() 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;
// 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;
// Add a container type to this namespace. Returns false only
// on error. Silently discards requests to add non-container types.
virtual bool MaybeAddContainerType(const std::string& type_name);
// Returns true iff this has a type for |type_name|.
virtual bool HasType(const std::string& type_name) const;
// Returns true iff |package| is a valid package name.
virtual bool IsValidPackage(const std::string& package) const;
// Returns true iff |raw_type| is a valid return type.
virtual bool IsValidReturnType(const AidlType& raw_type,
const std::string& filename) const;
// Returns true iff |arg_type| is a valid method argument.
virtual bool IsValidArg(const AidlArgument& a,
int arg_index,
const std::string& filename) const;
// Returns true if this is a container type, rather than a normal type.
virtual bool IsContainerType(const std::string& type_name) const;
// Returns true iff the name can be canonicalized to a container type.
virtual bool CanonicalizeContainerType(
const std::string& raw_type_name,
std::vector<std::string>* container_class,
std::vector<std::string>* contained_type_names) const;
protected:
TypeNamespace() = default;
virtual ~TypeNamespace() = default;
// Get a pointer to an existing type.
virtual const ValidatableType* GetValidatableType(
const std::string& name) 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 std::string& name) const;
protected:
bool Add(const T* type);
const ValidatableType* GetValidatableType(
const std::string& name) const override { return Find(name); }
private:
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 = Find(type->QualifiedName());
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->QualifiedName();
return false;
}
if (type->Kind() != existing->Kind()) {
LOG(ERROR) << type->DeclFile() << ":" << type->DeclLine()
<< " attempt to redefine " << type->QualifiedName()
<< " 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 std::string& raw_name) const {
using std::string;
using std::vector;
using android::base::Join;
using android::base::Trim;
string name = Trim(raw_name);
if (IsContainerType(name)) {
vector<string> container_class;
vector<string> contained_type_names;
if (!CanonicalizeContainerType(name, &container_class,
&contained_type_names)) {
return nullptr;
}
for (string& contained_type_name : contained_type_names) {
const T* contained_type = Find(contained_type_name);
if (!contained_type) {
return nullptr;
}
contained_type_name = contained_type->QualifiedName();
}
name = Join(container_class, '.') +
"<" + Join(contained_type_names, ',') + ">";
}
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->QualifiedName() == name) {
ret = type.get();
break;
}
// We allow authors to drop packages when refering to a class name.
if (type->Name() == name) {
ret = type.get();
}
}
return ret;
}
} // namespace aidl
} // namespace android
#endif // AIDL_TYPE_NAMESPACE_H_