Add toString to Java code
* toString is mainly for debugging purposes.
* For structs and interfaces, add Object.toString.
* For enums, add MyEnum.toString(int) and MyEnum.dumpBitfield(int).
Use them as follows:
* For enums, use the static method E.toString(int).
* For bitfields, use the static method E.dumpBitfield(int).
* For all arrays, use java.utils.Arrays.deepToString(o)
* For everything else, use one of the following:
* o.toString(), if o is not null
* Object.toString(o)
* String.valueOf(o)
* Note that for array / vec of enums / bitfields, the raw integer
value is dumped.
Bug: 33459772
Test: hidl_test_java
Change-Id: Ifb1ed519770b907e0a4e345b2c3109dc322a23b2
diff --git a/ArrayType.cpp b/ArrayType.cpp
index 7753031..b47ba3b 100644
--- a/ArrayType.cpp
+++ b/ArrayType.cpp
@@ -318,6 +318,17 @@
out << "}\n\n";
}
+void ArrayType::emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const {
+ out << streamName << ".append(java.util.Arrays."
+ << (countDimensions() > 1 ? "deepToString" : "toString")
+ << "("
+ << name << "));\n";
+}
+
+
bool ArrayType::needsEmbeddedReadWrite() const {
return mElementType->needsEmbeddedReadWrite();
}
diff --git a/ArrayType.h b/ArrayType.h
index e17df10..a0c2a08 100644
--- a/ArrayType.h
+++ b/ArrayType.h
@@ -95,6 +95,11 @@
const std::string &parentName,
const std::string &offsetText) const override;
+ void emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const override;
+
bool needsEmbeddedReadWrite() const override;
bool needsResolveReferences() const override;
bool resultNeedsDeref() const override;
diff --git a/CompoundType.cpp b/CompoundType.cpp
index 685ba85..dcd426a 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -619,6 +619,23 @@
////////////////////////////////////////////////////////////////////////////
+ out << "@Override\npublic final String toString() ";
+ out.block([&] {
+ out << "java.lang.StringBuilder builder = new java.lang.StringBuilder();\n"
+ << "builder.append(\"{\");\n";
+ for (const auto &field : *mFields) {
+ out << "builder.append(\"";
+ if (field != *(mFields->begin())) {
+ out << ", ";
+ }
+ out << "." << field->name() << " = \");\n";
+ field->type().emitJavaDump(out, "builder", "this." + field->name());
+ }
+ out << "builder.append(\"}\");\nreturn builder.toString();\n";
+ }).endl().endl();
+
+ ////////////////////////////////////////////////////////////////////////////
+
out << "public final void readFromParcel(android.os.HwParcel parcel) {\n";
out.indent();
out << "android.os.HwBlob blob = parcel.readBuffer();\n";
diff --git a/EnumType.cpp b/EnumType.cpp
index 7aa0231..0ef2590 100644
--- a/EnumType.cpp
+++ b/EnumType.cpp
@@ -32,6 +32,8 @@
: Scope(localName, location),
mValues(),
mStorageType(storageType) {
+ mBitfieldType = new BitFieldType();
+ mBitfieldType->setElementType(this);
}
const Type *EnumType::storageType() const {
@@ -101,6 +103,10 @@
return "TYPE_ENUM";
}
+BitFieldType *EnumType::getBitfieldType() const {
+ return mBitfieldType;
+}
+
LocalIdentifier *EnumType::lookupIdentifier(const std::string &name) const {
std::vector<const EnumType *> chain;
getTypeChain(&chain);
@@ -343,11 +349,13 @@
return OK;
}
-status_t EnumType::emitJavaTypeDeclarations(Formatter &out, bool) const {
+status_t EnumType::emitJavaTypeDeclarations(Formatter &out, bool atTopLevel) const {
const ScalarType *scalarType = mStorageType->resolveToScalarType();
CHECK(scalarType != NULL);
- out << "public final class "
+ out << "public "
+ << (atTopLevel ? "" : "static ")
+ << "final class "
<< localName()
<< " {\n";
@@ -385,6 +393,42 @@
}
}
+ out << "public static final String toString("
+ << typeName << " o) ";
+ out.block([&] {
+ for (EnumValue *value : values()) {
+ out.sIf("o == " + value->name(), [&] {
+ out << "return \"" << value->name() << "\";\n";
+ }).endl();
+ }
+ out << "return \"0x\" + ";
+ scalarType->emitConvertToJavaHexString(out, "o");
+ out << ";\n";
+ }).endl();
+
+ auto bitfieldType = getBitfieldType()->getJavaType(false /* forInitializer */);
+ auto bitfieldWrapperType = getBitfieldType()->getJavaWrapperType();
+ out << "\n"
+ << "public static final String dumpBitfield("
+ << bitfieldType << " o) ";
+ out.block([&] {
+ out << "java.util.ArrayList<String> list = new java.util.ArrayList<>();\n";
+ out << bitfieldType << " flipped = 0;\n";
+ for (EnumValue *value : values()) {
+ out.sIf("(o & " + value->name() + ") == " + value->name(), [&] {
+ out << "list.add(\"" << value->name() << "\");\n";
+ out << "flipped |= " << value->name() << ";\n";
+ }).endl();
+ }
+ // put remaining bits
+ out.sIf("o != flipped", [&] {
+ out << "list.add(\"0x\" + ";
+ scalarType->emitConvertToJavaHexString(out, "o & (~flipped)");
+ out << ");\n";
+ }).endl();
+ out << "return String.join(\" | \", list);\n";
+ }).endl().endl();
+
out.unindent();
out << "};\n\n";
@@ -435,6 +479,14 @@
return OK;
}
+void EnumType::emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const {
+ out << streamName << ".append(" << fqName().javaName() << ".toString("
+ << name << "));\n";
+}
+
void EnumType::getTypeChain(std::vector<const EnumType *> *out) const {
out->clear();
const EnumType *type = this;
@@ -741,6 +793,11 @@
true /* needsCast */);
}
+EnumType *BitFieldType::getEnumType() const {
+ CHECK(mElementType->isEnum());
+ return static_cast<EnumType *>(mElementType);
+}
+
// a bitfield maps to the underlying scalar type in C++, so operator<< is
// already defined. We can still emit useful information if the bitfield is
// in a struct / union by overriding emitDump as below.
@@ -748,13 +805,19 @@
Formatter &out,
const std::string &streamName,
const std::string &name) const {
- CHECK(mElementType->isEnum());
- const EnumType *enumType = static_cast<EnumType *>(mElementType);
- out << streamName << " += "<< enumType->fqName().cppNamespace()
- << "::toString<" << enumType->getCppStackType()
+ out << streamName << " += "<< getEnumType()->fqName().cppNamespace()
+ << "::toString<" << getEnumType()->getCppStackType()
<< ">(" << name << ");\n";
}
+void BitFieldType::emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const {
+ out << streamName << ".append(" << getEnumType()->fqName().javaName() << ".dumpBitfield("
+ << name << "));\n";
+}
+
void BitFieldType::emitJavaFieldReaderWriter(
Formatter &out,
size_t depth,
diff --git a/EnumType.h b/EnumType.h
index e259297..b853054 100644
--- a/EnumType.h
+++ b/EnumType.h
@@ -26,6 +26,7 @@
namespace android {
struct EnumValue;
+struct BitFieldType;
struct EnumType : public Scope {
EnumType(const char *localName,
@@ -56,6 +57,9 @@
std::string getVtsType() const override;
+ // Return the type that corresponds to bitfield<T>.
+ BitFieldType *getBitfieldType() const;
+
void emitReaderWriter(
Formatter &out,
const std::string &name,
@@ -83,6 +87,11 @@
status_t emitVtsTypeDeclarations(Formatter &out) const override;
status_t emitVtsAttributeType(Formatter &out) const override;
+ void emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const override;
+
void getAlignmentAndSize(size_t *align, size_t *size) const override;
void appendToExportedTypesVector(
@@ -106,6 +115,7 @@
std::vector<EnumValue *> mValues;
Type *mStorageType;
+ BitFieldType *mBitfieldType;
DISALLOW_COPY_AND_ASSIGN(EnumType);
};
@@ -159,6 +169,8 @@
std::string getVtsType() const override;
+ EnumType *getEnumType() const;
+
status_t emitVtsAttributeType(Formatter &out) const override;
void getAlignmentAndSize(size_t *align, size_t *size) const override;
@@ -176,6 +188,11 @@
const std::string &streamName,
const std::string &name) const override;
+ void emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const override;
+
void emitJavaFieldReaderWriter(
Formatter &out,
size_t depth,
diff --git a/ScalarType.cpp b/ScalarType.cpp
index 9b6c0c3..b8bd427 100644
--- a/ScalarType.cpp
+++ b/ScalarType.cpp
@@ -218,6 +218,40 @@
out << streamName << " += toHexString(" << name << ");\n";
}
+void ScalarType::emitConvertToJavaHexString(
+ Formatter &out,
+ const std::string &name) const {
+ switch(mKind) {
+ case KIND_BOOL: {
+ out << "((" << name << ") ? \"0x1\" : \"0x0\")";
+ break;
+ }
+ case KIND_INT8: // fallthrough
+ case KIND_UINT8: // fallthrough
+ case KIND_INT16: // fallthrough
+ case KIND_UINT16: {
+ // Because Byte and Short doesn't have toHexString, we have to use Integer.toHexString.
+ out << "Integer.toHexString(" << getJavaWrapperType() << ".toUnsignedInt(("
+ << getJavaType(false /* forInitializer */) << ")(" << name << ")))";
+ break;
+ }
+ case KIND_INT32: // fallthrough
+ case KIND_UINT32: // fallthrough
+ case KIND_INT64: // fallthrough
+ case KIND_UINT64: {
+ out << getJavaWrapperType() << ".toHexString(" << name << ")";
+ break;
+ }
+ case KIND_FLOAT: // fallthrough
+ case KIND_DOUBLE: // fallthrough
+ default: {
+ // no hex for floating point numbers.
+ out << name;
+ break;
+ }
+ }
+}
+
void ScalarType::emitJavaFieldReaderWriter(
Formatter &out,
size_t /* depth */,
diff --git a/ScalarType.h b/ScalarType.h
index 8b70d72..a8a5f68 100644
--- a/ScalarType.h
+++ b/ScalarType.h
@@ -85,6 +85,10 @@
const std::string &streamName,
const std::string &name) const;
+ void emitConvertToJavaHexString(
+ Formatter &out,
+ const std::string &name) const;
+
void emitJavaFieldReaderWriter(
Formatter &out,
size_t depth,
diff --git a/Type.cpp b/Type.cpp
index 71279c0..546d6ab 100644
--- a/Type.cpp
+++ b/Type.cpp
@@ -214,6 +214,13 @@
<< ");\n";
}
+void Type::emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const {
+ out << streamName << ".append(" << name << ");\n";
+}
+
bool Type::useParentInEmitResolveReferencesEmbedded() const {
return needsResolveReferences();
}
diff --git a/Type.h b/Type.h
index f926ed2..ce2a03a 100644
--- a/Type.h
+++ b/Type.h
@@ -158,6 +158,11 @@
const std::string &streamName,
const std::string &name) const;
+ virtual void emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const;
+
virtual bool useParentInEmitResolveReferencesEmbedded() const;
virtual bool useNameInEmitReaderWriterEmbedded(bool isReader) const;
diff --git a/generateJava.cpp b/generateJava.cpp
index 56ac2d6..3e8d493 100644
--- a/generateJava.cpp
+++ b/generateJava.cpp
@@ -303,6 +303,18 @@
out.unindent();
out << "}\n\n";
+
+ out << "@Override\npublic String toString() ";
+ out.block([&] {
+ out.sTry([&] {
+ out << "return this.interfaceDescriptor() + \"@Proxy\";\n";
+ }).sCatch("RemoteException ex", [&] {
+ out << "/* ignored; handled below. */\n";
+ }).endl();
+ out << "return \"[class or subclass of \" + "
+ << ifaceName << ".kInterfaceName + \"]@Proxy\";\n";
+ }).endl().endl();
+
const Interface *prevInterface = nullptr;
for (const auto &tuple : iface->allMethodsFromRoot()) {
const Method *method = tuple.method();
@@ -488,6 +500,11 @@
out.unindent();
out << "}\n\n";
+ out << "@Override\npublic String toString() ";
+ out.block([&] {
+ out << "return this.interfaceDescriptor() + \"@Stub\";\n";
+ }).endl().endl();
+
out << "@Override\n"
<< "public void onTransact("
<< "int _hidl_code, "
diff --git a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
index acafeae..4864371 100644
--- a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
+++ b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
@@ -25,6 +25,7 @@
import android.util.Log;
import java.util.ArrayList;
+import java.util.Arrays;
public final class HidlTestJava {
private static final String TAG = "HidlTestJava";
@@ -367,6 +368,7 @@
return;
}
+ System.err.printf("Expected '%s', got '%s'\n", s, result);
Log.e(TAG, "Expected '" + s + "', got '" + result + "'");
throw new RuntimeException();
}
@@ -759,6 +761,36 @@
ExpectTrue(structs.size() == 5);
ExpectTrue(structs.get(1).matrices.size() == 6);
+ {
+ IBaz.Everything e = new IBaz.Everything();
+ Expect(e.toString(),
+ "{.number = 0, .anotherNumber = 0, .s = , " +
+ ".vs = [], .multidimArray = [[null, null], [null, null]], " +
+ ".sArray = [null, null, null], .anotherStruct = {.first = , .last = }, .bf = }");
+ e.s = "string!";
+ e.number = 127;
+ e.anotherNumber = 100;
+ e.vs.addAll(Arrays.asList("One", "Two", "Three"));
+ for (int i = 0; i < e.multidimArray.length; i++)
+ for (int j = 0; j < e.multidimArray[i].length; j++)
+ e.multidimArray[i][j] = Integer.toString(i) + Integer.toString(j);
+ e.bf = IBaz.BitField.VALL;
+ e.anotherStruct.first = "James";
+ e.anotherStruct.last = "Bond";
+ Expect(e.toString(),
+ "{.number = 127, .anotherNumber = 100, .s = string!, " +
+ ".vs = [One, Two, Three], .multidimArray = [[00, 01], [10, 11]], " +
+ ".sArray = [null, null, null], .anotherStruct = {.first = James, .last = Bond}, " +
+ ".bf = V0 | V1 | V2 | V3 | VALL}");
+ Expect(IBaz.BitField.toString(IBaz.BitField.VALL), "VALL");
+ Expect(IBaz.BitField.toString((byte)(IBaz.BitField.V0 | IBaz.BitField.V2)), "0x5");
+ Expect(IBaz.BitField.dumpBitfield(IBaz.BitField.VALL), "V0 | V1 | V2 | V3 | VALL");
+ Expect(IBaz.BitField.dumpBitfield((byte)(IBaz.BitField.V1 | IBaz.BitField.V3 | 0xF0)),
+ "V1 | V3 | 0xf0");
+
+ Expect(proxy.toString(), IBaz.kInterfaceName + "@Proxy");
+ }
+
// --- DEATH RECIPIENT TESTING ---
// This must always be done last, since it will kill the native server process
HidlDeathRecipient recipient1 = new HidlDeathRecipient();
diff --git a/utils/Formatter.cpp b/utils/Formatter.cpp
index df3a5a4..4d3771b 100644
--- a/utils/Formatter.cpp
+++ b/utils/Formatter.cpp
@@ -86,6 +86,16 @@
return this->block(block);
}
+Formatter &Formatter::sTry(std::function<void(void)> block) {
+ (*this) << "try ";
+ return this->block(block);
+}
+
+Formatter &Formatter::sCatch(const std::string &exception, std::function<void(void)> block) {
+ (*this) << " catch (" << exception << ") ";
+ return this->block(block);
+}
+
Formatter &Formatter::operator<<(const std::string &out) {
const size_t len = out.length();
size_t start = 0;
diff --git a/utils/include/hidl-util/Formatter.h b/utils/include/hidl-util/Formatter.h
index 7a27287..d1f76fb 100644
--- a/utils/include/hidl-util/Formatter.h
+++ b/utils/include/hidl-util/Formatter.h
@@ -79,6 +79,15 @@
Formatter &sElseIf(const std::string &cond, std::function<void(void)> block);
Formatter &sElse(std::function<void(void)> block);
+ // out.sTry([&] {
+ // out << "throw RemoteException();\n"
+ // }).sCatch("RemoteException ex", [&] {
+ // out << "ex.printStackTrace();\n"
+ // }).endl();
+ // note that there will be a space before the "catch"-s.
+ Formatter &sTry(std::function<void(void)> block);
+ Formatter &sCatch(const std::string &exception, std::function<void(void)> block);
+
Formatter &operator<<(const std::string &out);
Formatter &operator<<(size_t n);