blob: 59d6d62ead74eed73927966a2f1e269f459f4ca4 [file] [log] [blame]
#include "aidl_language.h"
#include "aidl_typenames.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cassert>
#include <iostream>
#include <string>
#include <utility>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include "aidl_language_y.h"
#include "logging.h"
#ifdef _WIN32
int isatty(int fd)
{
return (fd == 0);
}
#endif
using android::aidl::IoDelegate;
using android::base::Join;
using android::base::Split;
using std::cerr;
using std::endl;
using std::pair;
using std::string;
using std::unique_ptr;
using std::vector;
void yylex_init(void **);
void yylex_destroy(void *);
void yyset_in(FILE *f, void *);
int yyparse(Parser*);
YY_BUFFER_STATE yy_scan_buffer(char *, size_t, void *);
void yy_delete_buffer(YY_BUFFER_STATE, void *);
AidlToken::AidlToken(const std::string& text, const std::string& comments)
: text_(text),
comments_(comments) {}
AidlTypeSpecifier::AidlTypeSpecifier(const string& unresolved_name, bool is_array,
vector<unique_ptr<AidlTypeSpecifier>>* type_params,
unsigned line, const string& comments)
: unresolved_name_(unresolved_name),
is_array_(is_array),
type_params_(type_params),
line_(line),
comments_(comments) {}
string AidlTypeSpecifier::ToString() const {
string ret = GetName();
if (IsGeneric()) {
vector<string> arg_names;
for (const auto& ta : GetTypeParameters()) {
arg_names.emplace_back(ta->ToString());
}
ret += "<" + Join(arg_names, ",") + ">";
}
if (IsArray()) {
ret += "[]";
}
return ret;
}
bool AidlTypeSpecifier::Resolve(android::aidl::AidlTypenames& typenames) {
assert(!IsResolved());
pair<string, bool> result = typenames.ResolveTypename(unresolved_name_);
if (result.second) {
fully_qualified_name_ = result.first;
}
return result.second;
}
AidlVariableDeclaration::AidlVariableDeclaration(AidlTypeSpecifier* type, std::string name,
unsigned line)
: type_(type), name_(name), line_(line) {}
string AidlVariableDeclaration::ToString() const {
return type_->ToString() + " " + name_;
}
AidlArgument::AidlArgument(AidlArgument::Direction direction, AidlTypeSpecifier* type,
std::string name, unsigned line)
: AidlVariableDeclaration(type, name, line),
direction_(direction),
direction_specified_(true) {}
AidlArgument::AidlArgument(AidlTypeSpecifier* type, std::string name, unsigned line)
: AidlVariableDeclaration(type, name, line),
direction_(AidlArgument::IN_DIR),
direction_specified_(false) {}
string AidlArgument::ToString() const {
string ret;
if (direction_specified_) {
switch(direction_) {
case AidlArgument::IN_DIR:
ret += "in ";
break;
case AidlArgument::OUT_DIR:
ret += "out ";
break;
case AidlArgument::INOUT_DIR:
ret += "inout ";
break;
}
}
ret += AidlVariableDeclaration::ToString();
return ret;
}
AidlIntConstant::AidlIntConstant(std::string name, int32_t value)
: name_(name),
value_(value),
is_valid_(true) {}
AidlIntConstant::AidlIntConstant(std::string name,
std::string value,
unsigned line_number)
: name_(name) {
uint32_t unsigned_val;
if (!android::base::ParseUint(value.c_str(), &unsigned_val)) {
is_valid_ = false;
LOG(ERROR) << "Found invalid int value '" << value
<< "' on line " << line_number;
} else {
// Converting from unsigned to signed integer.
value_ = unsigned_val;
is_valid_ = true;
}
}
AidlStringConstant::AidlStringConstant(std::string name,
std::string value,
unsigned line_number)
: name_(name),
value_(value) {
is_valid_ = true;
for (size_t i = 0; i < value_.length(); ++i) {
const char& c = value_[i];
if (c <= 0x1f || // control characters are < 0x20
c >= 0x7f || // DEL is 0x7f
c == '\\') { // Disallow backslashes for future proofing.
LOG(ERROR) << "Found invalid character at index " << i
<< " in string constant '" << value_
<< "' beginning on line " << line_number;
is_valid_ = false;
break;
}
}
}
AidlMethod::AidlMethod(bool oneway, AidlTypeSpecifier* type, std::string name,
std::vector<std::unique_ptr<AidlArgument>>* args, unsigned line,
const std::string& comments, int id)
: oneway_(oneway),
comments_(comments),
type_(type),
name_(name),
line_(line),
arguments_(std::move(*args)),
id_(id) {
has_id_ = true;
delete args;
for (const unique_ptr<AidlArgument>& a : arguments_) {
if (a->IsIn()) { in_arguments_.push_back(a.get()); }
if (a->IsOut()) { out_arguments_.push_back(a.get()); }
}
}
AidlMethod::AidlMethod(bool oneway, AidlTypeSpecifier* type, std::string name,
std::vector<std::unique_ptr<AidlArgument>>* args, unsigned line,
const std::string& comments)
: AidlMethod(oneway, type, name, args, line, comments, 0) {
has_id_ = false;
}
Parser::Parser(const IoDelegate& io_delegate, android::aidl::AidlTypenames* typenames)
: io_delegate_(io_delegate), typenames_(typenames) {
yylex_init(&scanner_);
}
AidlDefinedType::AidlDefinedType(std::string name, unsigned line, const std::string& comments,
const std::vector<std::string>& package)
: name_(name), line_(line), comments_(comments), package_(package) {}
std::string AidlDefinedType::GetPackage() const {
return Join(package_, '.');
}
std::string AidlDefinedType::GetCanonicalName() const {
if (package_.empty()) {
return GetName();
}
return GetPackage() + "." + GetName();
}
AidlParcelable::AidlParcelable(AidlQualifiedName* name, unsigned line,
const std::vector<std::string>& package,
const std::string& cpp_header)
: AidlDefinedType(name->GetDotName(), line, "" /*comments*/, package),
name_(name),
cpp_header_(cpp_header) {
// Strip off quotation marks if we actually have a cpp header.
if (cpp_header_.length() >= 2) {
cpp_header_ = cpp_header_.substr(1, cpp_header_.length() - 2);
}
}
AidlStructuredParcelable::AidlStructuredParcelable(
AidlQualifiedName* name, unsigned line, const std::vector<std::string>& package,
std::vector<std::unique_ptr<AidlVariableDeclaration>>* variables)
: AidlParcelable(name, line, package, "" /*cpp_header*/), variables_(std::move(*variables)) {}
AidlInterface::AidlInterface(const std::string& name, unsigned line,
const std::string& comments, bool oneway,
std::vector<std::unique_ptr<AidlMember>>* members,
const std::vector<std::string>& package)
: AidlDefinedType(name, line, comments, package),
oneway_(oneway) {
for (auto& member : *members) {
AidlMember* local = member.release();
AidlMethod* method = local->AsMethod();
AidlIntConstant* int_constant = local->AsIntConstant();
AidlStringConstant* string_constant = local->AsStringConstant();
if (method) {
methods_.emplace_back(method);
} else if (int_constant) {
int_constants_.emplace_back(int_constant);
} else if (string_constant) {
string_constants_.emplace_back(string_constant);
} else {
LOG(FATAL) << "Member is neither method nor constant!";
}
}
delete members;
}
AidlDefinedType* AidlDocument::ReleaseDefinedType() {
if (defined_types_.size() == 0) {
return nullptr;
}
if (defined_types_.size() > 1) {
LOG(ERROR) << "AIDL only supports compiling one defined type per file.";
return nullptr;
}
return defined_types_[0].release();
}
AidlQualifiedName::AidlQualifiedName(std::string term,
std::string comments)
: terms_({term}),
comments_(comments) {
if (term.find('.') != string::npos) {
terms_ = Split(term, ".");
for (const auto& term: terms_) {
if (term.empty()) {
LOG(FATAL) << "Malformed qualified identifier: '" << term << "'";
}
}
}
}
void AidlQualifiedName::AddTerm(const std::string& term) {
terms_.push_back(term);
}
AidlImport::AidlImport(const std::string& from,
const std::string& needed_class, unsigned line)
: from_(from),
needed_class_(needed_class),
line_(line) {}
Parser::~Parser() {
if (raw_buffer_) {
yy_delete_buffer(buffer_, scanner_);
raw_buffer_.reset();
}
yylex_destroy(scanner_);
}
bool Parser::ParseFile(const string& filename) {
// Make sure we can read the file first, before trashing previous state.
unique_ptr<string> new_buffer = io_delegate_.GetFileContents(filename);
if (!new_buffer) {
LOG(ERROR) << "Error while opening file for parsing: '" << filename << "'";
return false;
}
// Throw away old parsing state if we have any.
if (raw_buffer_) {
yy_delete_buffer(buffer_, scanner_);
raw_buffer_.reset();
}
raw_buffer_ = std::move(new_buffer);
// We're going to scan this buffer in place, and yacc demands we put two
// nulls at the end.
raw_buffer_->append(2u, '\0');
filename_ = filename;
package_.reset();
error_ = 0;
document_.reset();
buffer_ = yy_scan_buffer(&(*raw_buffer_)[0], raw_buffer_->length(), scanner_);
if (yy::parser(this).parse() != 0 || error_ != 0)
return false;
if (document_.get() == nullptr) {
LOG(ERROR) << "Parser succeeded but yielded no document!";
return false;
}
return true;
}
std::vector<std::string> Parser::Package() const {
if (!package_) {
return {};
}
return package_->GetTerms();
}
void Parser::AddImport(AidlQualifiedName* name, unsigned line) {
imports_.emplace_back(new AidlImport(this->FileName(),
name->GetDotName(), line));
delete name;
}
bool Parser::Resolve() {
bool success = true;
for (AidlTypeSpecifier* typespec : unresolved_typespecs_) {
if (!typespec->Resolve(*typenames_)) {
LOG(ERROR) << "Failed to resolve '" << typespec->GetUnresolvedName() << "' at "
<< this->FileName() << ":" << typespec->GetLine();
success = false;
// don't stop to show more errors if any
}
}
return success;
}