/*
* Copyright (C) 2016 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 "RefType.h"
#include "ArrayType.h"
#include "CompoundType.h"
#include <hidl-util/Formatter.h>
#include <android-base/logging.h>
namespace android {
RefType::RefType(Scope* parent) : TemplatedType(parent) {}
std::string RefType::templatedTypeName() const {
return "ref";
}
std::vector<const Reference<Type>*> RefType::getStrongReferences() const {
return {};
}
std::string RefType::getVtsType() const {
return "TYPE_REF";
}
std::string RefType::getVtsValueName() const {
return "ref_value";
}
bool RefType::isCompatibleElementType(const Type* elementType) const {
if (elementType->isScalar()) {
return true;
}
if (elementType->isString()) {
return true;
}
if (elementType->isEnum()) {
return true;
}
if (elementType->isBitField()) {
return true;
}
if (elementType->isCompoundType() &&
static_cast<const CompoundType*>(elementType)->style() == CompoundType::STYLE_STRUCT) {
return true;
}
if (elementType->isTemplatedType()) {
return this->isCompatibleElementType(
static_cast<const TemplatedType*>(elementType)->getElementType());
}
if (elementType->isArray()) {
return this->isCompatibleElementType(
static_cast<const ArrayType*>(elementType)->getElementType());
}
return false;
}
/* return something like "T const *".
* The reason we don't return "const T *" is to handle cases like
* ref<ref<ref<T>>> t_3ptr;
* in this case the const's will get stacked on the left (const const const T *** t_3ptr)
* but in this implementation it would be clearer (T const* const* const* t_3ptr) */
std::string RefType::getCppType(StorageMode /*mode*/, bool specifyNamespaces) const {
return mElementType->getCppStackType(specifyNamespaces)
+ " const*";
}
void RefType::emitReaderWriter(
Formatter &,
const std::string &,
const std::string &,
bool,
bool,
ErrorMode) const {
// RefType doesn't get read / written at this stage.
return;
}
void RefType::emitResolveReferences(
Formatter &out,
const std::string &name,
bool nameIsPointer,
const std::string &parcelObj,
bool parcelObjIsPointer,
bool isReader,
ErrorMode mode) const {
emitResolveReferencesEmbedded(
out,
0 /* depth */,
name,
name /* sanitizedName */,
nameIsPointer,
parcelObj,
parcelObjIsPointer,
isReader,
mode,
"", // parentName
""); // offsetText
}
void RefType::emitResolveReferencesEmbedded(
Formatter &out,
size_t /* depth */,
const std::string &name,
const std::string &sanitizedName,
bool /*nameIsPointer*/,
const std::string &parcelObj,
bool parcelObjIsPointer,
bool isReader,
ErrorMode mode,
const std::string &parentName,
const std::string &offsetText) const {
std::string elementType = mElementType->getCppStackType();
std::string baseType = getCppStackType();
const std::string parcelObjDeref =
parcelObjIsPointer ? ("*" + parcelObj) : parcelObj;
const std::string parcelObjPointer =
parcelObjIsPointer ? parcelObj : ("&" + parcelObj);
// as if nameIsPointer is false. Pointers are always pass by values,
// so name is always the pointer value itself. Hence nameIsPointer = false.
const std::string namePointer = "&" + name;
const std::string handleName = "_hidl_" + sanitizedName + "__ref_handle";
const std::string resolveBufName = "_hidl_" + sanitizedName + "__ref_resolve_buf";
bool isEmbedded = (!parentName.empty() && !offsetText.empty());
out << "size_t " << handleName << ";\n"
<< "bool " << resolveBufName << ";\n\n";
out << "_hidl_err = ";
if (isReader) {
out << "::android::hardware::read"
<< (isEmbedded ? "Embedded" : "")
<< "ReferenceFromParcel<"
<< elementType
<< ">(const_cast<"
<< baseType
<< " *>("
<< namePointer
<< "),";
} else {
out << "::android::hardware::write"
<< (isEmbedded ? "Embedded" : "")
<< "ReferenceToParcel<"
<< elementType
<< ">("
<< name
<< ",";
}
out.indent();
out.indent();
out << (isReader ? parcelObjDeref : parcelObjPointer);
if(isEmbedded)
out << ",\n"
<< parentName
<< ",\n"
<< offsetText;
out << ",\n&" + handleName;
out << ",\n&" + resolveBufName;
out << ");\n\n";
out.unindent();
out.unindent();
handleError(out, mode);
if(!mElementType->needsResolveReferences() && !mElementType->needsEmbeddedReadWrite())
return; // no need to deal with element type recursively.
out << "if(" << resolveBufName << ") {\n";
out.indent();
if(mElementType->needsEmbeddedReadWrite()) {
mElementType->emitReaderWriterEmbedded(
out,
0 /* depth */,
name,
sanitizedName,
true /* nameIsPointer */, // for element type, name is a pointer.
parcelObj,
parcelObjIsPointer,
isReader,
mode,
handleName,
"0 /* parentOffset */");
}
if(mElementType->needsResolveReferences()) {
mElementType->emitResolveReferencesEmbedded(
out,
0 /* depth */,
"(*" + name + ")",
sanitizedName + "_deref",
false /* nameIsPointer */,
// must deref it and say false here, otherwise pointer to pointers don't work
parcelObj,
parcelObjIsPointer,
isReader,
mode,
handleName,
"0 /* parentOffset */");
}
out.unindent();
out << "}\n\n";
}
bool RefType::deepNeedsResolveReferences(std::unordered_set<const Type*>* /* visited */) const {
return true;
}
bool RefType::needsEmbeddedReadWrite() const {
return false;
}
bool RefType::resultNeedsDeref() const {
return false;
}
bool RefType::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
return false;
}
bool RefType::deepContainsPointer(std::unordered_set<const Type*>* /* visited */) const {
return true;
}
} // namespace android