summaryrefslogtreecommitdiff
path: root/tools/codegen/src
diff options
context:
space:
mode:
authorSteven Laver <lavers@google.com>2019-11-05 13:42:59 -0800
committerSteven Laver <lavers@google.com>2019-11-09 01:16:30 +0000
commit7c6cc72e18cc1df5205fd2bc47664e6cc2534ad2 (patch)
treefc34e4ad6037cf231cccc3b56ccd13e82917520a /tools/codegen/src
parent8f4f93bf3ba75d8e83cb0a8618cb80f226ada5ac (diff)
parentda5e1bd24a9a0ca24e7e49fad9e604409e573376 (diff)
Merge RP1A.191024.001
Change-Id: I5cda3bba276e99d948b752be87d4599e9f882e0f
Diffstat (limited to 'tools/codegen/src')
-rw-r--r--tools/codegen/src/com/android/codegen/ClassInfo.kt5
-rw-r--r--tools/codegen/src/com/android/codegen/ClassPrinter.kt10
-rw-r--r--tools/codegen/src/com/android/codegen/FieldInfo.kt2
-rw-r--r--tools/codegen/src/com/android/codegen/Generators.kt241
-rw-r--r--tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt8
-rwxr-xr-xtools/codegen/src/com/android/codegen/Main.kt18
-rw-r--r--tools/codegen/src/com/android/codegen/SharedConstants.kt2
-rw-r--r--tools/codegen/src/com/android/codegen/Utils.kt12
8 files changed, 176 insertions, 122 deletions
diff --git a/tools/codegen/src/com/android/codegen/ClassInfo.kt b/tools/codegen/src/com/android/codegen/ClassInfo.kt
index 5061be2091e5..92da9dab863b 100644
--- a/tools/codegen/src/com/android/codegen/ClassInfo.kt
+++ b/tools/codegen/src/com/android/codegen/ClassInfo.kt
@@ -38,6 +38,11 @@ open class ClassInfo(val sourceLines: List<String>) {
val superInterfaces = (fileAst.types[0] as ClassOrInterfaceDeclaration)
.implementedTypes.map { it.asString() }
+ val superClass = run {
+ val superClasses = (fileAst.types[0] as ClassOrInterfaceDeclaration).extendedTypes
+ if (superClasses.isNonEmpty) superClasses[0] else null
+ }
+
val ClassName = classAst.nameAsString
private val genericArgsAst = classAst.typeParameters
val genericArgs = if (genericArgsAst.isEmpty()) "" else {
diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt
index 1f0d4b8a7ec9..bd72d9e7ec21 100644
--- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt
+++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt
@@ -1,6 +1,7 @@
package com.android.codegen
import com.github.javaparser.ast.Modifier
+import com.github.javaparser.ast.body.CallableDeclaration
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
import com.github.javaparser.ast.body.TypeDeclaration
import com.github.javaparser.ast.expr.*
@@ -37,6 +38,7 @@ class ClassPrinter(
val GeneratedMember by lazy { classRef("com.android.internal.util.DataClass.Generated.Member") }
val Parcelling by lazy { classRef("com.android.internal.util.Parcelling") }
val Parcelable by lazy { classRef("android.os.Parcelable") }
+ val Parcel by lazy { classRef("android.os.Parcel") }
val UnsupportedAppUsage by lazy { classRef("android.annotation.UnsupportedAppUsage") }
init {
@@ -354,7 +356,9 @@ class ClassPrinter(
}
fun hasMethod(name: String, vararg argTypes: String): Boolean {
- return classAst.methods.any {
+ val members: List<CallableDeclaration<*>> =
+ if (name == ClassName) classAst.constructors else classAst.methods
+ return members.any {
it.name.asString() == name &&
it.parameters.map { it.type.asString() } == argTypes.toList()
}
@@ -365,6 +369,10 @@ class ClassPrinter(
.mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) }
.filter { hasMethod("lazyInit${it.NameUpperCamel}") }
+ val extendsParcelableClass by lazy {
+ Parcelable !in superInterfaces && superClass != null
+ }
+
init {
val builderFactoryOverride = classAst.methods.find {
it.isStatic && it.nameAsString == "builder"
diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt
index ba00264f5f5e..1a7fd6e241aa 100644
--- a/tools/codegen/src/com/android/codegen/FieldInfo.kt
+++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt
@@ -191,7 +191,7 @@ data class FieldInfo(
* Parcel.write* and Parcel.read* method name wildcard values
*/
val ParcelMethodsSuffix = when {
- FieldClass in PRIMITIVE_TYPES - "char" - "boolean" +
+ FieldClass in PRIMITIVE_TYPES - "char" - "boolean" + BOXED_PRIMITIVE_TYPES +
listOf("String", "CharSequence", "Exception", "Size", "SizeF", "Bundle",
"FileDescriptor", "SparseBooleanArray", "SparseIntArray", "SparseArray") ->
FieldClass
diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt
index 914e475cfe41..431f378a8811 100644
--- a/tools/codegen/src/com/android/codegen/Generators.kt
+++ b/tools/codegen/src/com/android/codegen/Generators.kt
@@ -341,7 +341,7 @@ private fun ClassPrinter.generateBuilderSetters(visibility: String) {
}
}
- if (Type.contains("Map<")) {
+ if (FieldClass.endsWith("Map") && FieldInnerType != null) {
generateBuilderMethod(
name = adderName,
defVisibility = visibility,
@@ -417,11 +417,15 @@ fun ClassPrinter.generateParcelable() {
if (!isMethodGenerationSuppressed("writeToParcel", Parcel, "int")) {
+"@Override"
+GENERATED_MEMBER_HEADER
- "public void writeToParcel($Parcel dest, int flags)" {
+ "public void writeToParcel(@$NonNull $Parcel dest, int flags)" {
+"// You can override field parcelling by defining methods like:"
+"// void parcelFieldName(Parcel dest, int flags) { ... }"
+""
+ if (extendsParcelableClass) {
+ +"super.writeToParcel(dest, flags);\n"
+ }
+
if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) {
+"$flagStorageType flg = 0;"
booleanFields.forEachApply {
@@ -463,6 +467,123 @@ fun ClassPrinter.generateParcelable() {
+""
}
+ if (!hasMethod(ClassName, Parcel)) {
+ val visibility = if (classAst.isFinal) "/* package-private */" else "protected"
+
+ +"/** @hide */"
+ +"@SuppressWarnings({\"unchecked\", \"RedundantCast\"})"
+ +GENERATED_MEMBER_HEADER
+ "$visibility $ClassName(@$NonNull $Parcel in) {" {
+ +"// You can override field unparcelling by defining methods like:"
+ +"// static FieldType unparcelFieldName(Parcel in) { ... }"
+ +""
+
+ if (extendsParcelableClass) {
+ +"super(in);\n"
+ }
+
+ if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) {
+ +"$flagStorageType flg = in.read$FlagStorageType();"
+ }
+ booleanFields.forEachApply {
+ +"$Type $_name = (flg & $fieldBit) != 0;"
+ }
+ nonBooleanFields.forEachApply {
+
+ // Handle customized parceling
+ val customParcellingMethod = "unparcel$NameUpperCamel"
+ if (hasMethod(customParcellingMethod, Parcel)) {
+ +"$Type $_name = $customParcellingMethod(in);"
+ } else if (customParcellingClass != null) {
+ +"$Type $_name = $sParcelling.unparcel(in);"
+ } else if (hasAnnotation("@$DataClassEnum")) {
+ val ordinal = "${_name}Ordinal"
+ +"int $ordinal = in.readInt();"
+ +"$Type $_name = $ordinal < 0 ? null : $FieldClass.values()[$ordinal];"
+ } else {
+ val methodArgs = mutableListOf<String>()
+
+ // Create container if any
+ val containerInitExpr = when {
+ FieldClass.endsWith("Map") -> "new $LinkedHashMap<>()"
+ FieldClass == "List" || FieldClass == "ArrayList" ->
+ "new ${classRef("java.util.ArrayList")}<>()"
+ else -> ""
+ }
+ val passContainer = containerInitExpr.isNotEmpty()
+
+ // nullcheck +
+ // "FieldType fieldName = (FieldType)"
+ if (passContainer) {
+ methodArgs.add(_name)
+ !"$Type $_name = "
+ if (mayBeNull) {
+ +"null;"
+ !"if ((flg & $fieldBit) != 0) {"
+ pushIndent()
+ +""
+ !"$_name = "
+ }
+ +"$containerInitExpr;"
+ } else {
+ !"$Type $_name = "
+ if (mayBeNull) !"(flg & $fieldBit) == 0 ? null : "
+ if (ParcelMethodsSuffix == "StrongInterface") {
+ !"$FieldClass.Stub.asInterface("
+ } else if (Type !in PRIMITIVE_TYPES + "String" + "Bundle" &&
+ (!isArray || FieldInnerType !in PRIMITIVE_TYPES + "String") &&
+ ParcelMethodsSuffix != "Parcelable") {
+ !"($FieldClass) "
+ }
+ }
+
+ // Determine method args
+ when {
+ ParcelMethodsSuffix == "Parcelable" ->
+ methodArgs += "$FieldClass.class.getClassLoader()"
+ ParcelMethodsSuffix == "SparseArray" ->
+ methodArgs += "$FieldInnerClass.class.getClassLoader()"
+ ParcelMethodsSuffix == "TypedObject" ->
+ methodArgs += "$FieldClass.CREATOR"
+ ParcelMethodsSuffix == "TypedArray" ->
+ methodArgs += "$FieldInnerClass.CREATOR"
+ ParcelMethodsSuffix == "Map" ->
+ methodArgs += "${fieldTypeGenegicArgs[1].substringBefore("<")}.class.getClassLoader()"
+ ParcelMethodsSuffix.startsWith("Parcelable")
+ || (isList || isArray)
+ && FieldInnerType !in PRIMITIVE_TYPES + "String" ->
+ methodArgs += "$FieldInnerClass.class.getClassLoader()"
+ }
+
+ // ...in.readFieldType(args...);
+ when {
+ ParcelMethodsSuffix == "StrongInterface" -> !"in.readStrongBinder"
+ isArray -> !"in.create$ParcelMethodsSuffix"
+ else -> !"in.read$ParcelMethodsSuffix"
+ }
+ !"(${methodArgs.joinToString(", ")})"
+ if (ParcelMethodsSuffix == "StrongInterface") !")"
+ +";"
+
+ // Cleanup if passContainer
+ if (passContainer && mayBeNull) {
+ popIndent()
+ rmEmptyLine()
+ +"\n}"
+ }
+ }
+ }
+
+ +""
+ fields.forEachApply {
+ !"this."
+ generateSetFrom(_name)
+ }
+
+ generateOnConstructedCallback()
+ }
+ }
+
if (classAst.fields.none { it.variables[0].nameAsString == "CREATOR" }) {
val Creator = classRef("android.os.Parcelable.Creator")
@@ -477,104 +598,8 @@ fun ClassPrinter.generateParcelable() {
}
+"@Override"
- +"@SuppressWarnings({\"unchecked\", \"RedundantCast\"})"
- "public $ClassName createFromParcel($Parcel in)" {
- +"// You can override field unparcelling by defining methods like:"
- +"// static FieldType unparcelFieldName(Parcel in) { ... }"
- +""
- if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) {
- +"$flagStorageType flg = in.read$FlagStorageType();"
- }
- booleanFields.forEachApply {
- +"$Type $_name = (flg & $fieldBit) != 0;"
- }
- nonBooleanFields.forEachApply {
-
- // Handle customized parceling
- val customParcellingMethod = "unparcel$NameUpperCamel"
- if (hasMethod(customParcellingMethod, Parcel)) {
- +"$Type $_name = $customParcellingMethod(in);"
- } else if (customParcellingClass != null) {
- +"$Type $_name = $sParcelling.unparcel(in);"
- } else if (hasAnnotation("@$DataClassEnum")) {
- val ordinal = "${_name}Ordinal"
- +"int $ordinal = in.readInt();"
- +"$Type $_name = $ordinal < 0 ? null : $FieldClass.values()[$ordinal];"
- } else {
- val methodArgs = mutableListOf<String>()
-
- // Create container if any
- val containerInitExpr = when {
- FieldClass.endsWith("Map") -> "new $LinkedHashMap<>()"
- FieldClass == "List" || FieldClass == "ArrayList" ->
- "new ${classRef("java.util.ArrayList")}<>()"
- else -> ""
- }
- val passContainer = containerInitExpr.isNotEmpty()
-
- // nullcheck +
- // "FieldType fieldName = (FieldType)"
- if (passContainer) {
- methodArgs.add(_name)
- !"$Type $_name = "
- if (mayBeNull) {
- +"null;"
- !"if ((flg & $fieldBit) != 0) {"
- pushIndent()
- +""
- !"$_name = "
- }
- +"$containerInitExpr;"
- } else {
- !"$Type $_name = "
- if (mayBeNull) !"(flg & $fieldBit) == 0 ? null : "
- if (ParcelMethodsSuffix == "StrongInterface") {
- !"$FieldClass.Stub.asInterface("
- } else if (Type !in PRIMITIVE_TYPES + "String" + "Bundle" &&
- (!isArray || FieldInnerType !in PRIMITIVE_TYPES + "String") &&
- ParcelMethodsSuffix != "Parcelable") {
- !"($Type) "
- }
- }
-
- // Determine method args
- when {
- ParcelMethodsSuffix == "Parcelable" ->
- methodArgs += "$FieldClass.class.getClassLoader()"
- ParcelMethodsSuffix == "TypedObject" ->
- methodArgs += "$FieldClass.CREATOR"
- ParcelMethodsSuffix == "TypedArray" ->
- methodArgs += "$FieldInnerClass.CREATOR"
- ParcelMethodsSuffix.startsWith("Parcelable")
- || FieldClass == "Map"
- || (isList || isArray)
- && FieldInnerType !in PRIMITIVE_TYPES + "String" ->
- methodArgs += "$FieldInnerClass.class.getClassLoader()"
- }
-
- // ...in.readFieldType(args...);
- when {
- ParcelMethodsSuffix == "StrongInterface" -> !"in.readStrongBinder"
- isArray -> !"in.create$ParcelMethodsSuffix"
- else -> !"in.read$ParcelMethodsSuffix"
- }
- !"(${methodArgs.joinToString(", ")})"
- if (ParcelMethodsSuffix == "StrongInterface") !")"
- +";"
-
- // Cleanup if passContainer
- if (passContainer && mayBeNull) {
- popIndent()
- rmEmptyLine()
- +"\n}"
- }
- }
- }
- "return new $ClassType(" {
- fields.forEachTrimmingTrailingComma {
- +"$_name,"
- }
- } + ";"
+ "public $ClassName createFromParcel(@$NonNull $Parcel in)" {
+ +"return new $ClassName(in);"
}
rmEmptyLine()
} + ";"
@@ -586,7 +611,7 @@ fun ClassPrinter.generateEqualsHashcode() {
if (!isMethodGenerationSuppressed("equals", "Object")) {
+"@Override"
+GENERATED_MEMBER_HEADER
- "public boolean equals(Object o)" {
+ "public boolean equals(@$Nullable Object o)" {
+"// You can override field equality logic by defining either of the methods like:"
+"// boolean fieldNameEquals($ClassName other) { ... }"
+"// boolean fieldNameEquals(FieldType otherValue) { ... }"
@@ -829,6 +854,7 @@ private fun ClassPrinter.generateFieldValidation(field: FieldInfo) = field.run {
it.nameAsString == intOrStringDef?.AnnotationName
|| it.nameAsString in knownNonValidationAnnotations
|| it in perElementValidations
+ || it.args.any { (_, value) -> value is ArrayInitializerExpr }
}.forEach { annotation ->
appendValidateCall(annotation,
valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name)
@@ -849,14 +875,7 @@ fun ClassPrinter.appendValidateCall(annotation: AnnotationExpr, valueToValidate:
val validate = memberRef("com.android.internal.util.AnnotationValidations.validate")
"$validate(" {
!"${annotation.nameAsString}.class, null, $valueToValidate"
- val params = when (annotation) {
- is MarkerAnnotationExpr -> emptyMap()
- is SingleMemberAnnotationExpr -> mapOf("value" to annotation.memberValue)
- is NormalAnnotationExpr ->
- annotation.pairs.map { it.name.asString() to it.value }.toMap()
- else -> throw IllegalStateException()
- }
- params.forEach { name, value ->
+ annotation.args.forEach { name, value ->
!",\n\"$name\", $value"
}
}
@@ -885,7 +904,7 @@ fun ClassPrinter.generateForEachField() {
usedSpecializationsSet.toList().forEachLastAware { specType, isLast ->
val SpecType = specType.capitalize()
val ActionClass = classRef("com.android.internal.util.DataClass.Per${SpecType}FieldAction")
- +"$ActionClass<$ClassType> action$SpecType${if_(!isLast, ",")}"
+ +"@$NonNull $ActionClass<$ClassType> action$SpecType${if_(!isLast, ",")}"
}
}; " {" {
usedSpecializations.forEachIndexed { i, specType ->
@@ -900,7 +919,7 @@ fun ClassPrinter.generateForEachField() {
+"/** @deprecated May cause boxing allocations - use with caution! */"
+"@Deprecated"
+GENERATED_MEMBER_HEADER
- "void forEachField($PerObjectFieldAction<$ClassType> action)" {
+ "void forEachField(@$NonNull $PerObjectFieldAction<$ClassType> action)" {
fields.forEachApply {
+"action.acceptObject(this, \"$nameLowerCamel\", $name);"
}
diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
index 24cf4690dae1..d6953c00fc0b 100644
--- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
+++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
@@ -87,6 +87,14 @@ private fun ClassPrinter.appendExpr(sb: StringBuilder, ex: Expression?) {
is IntegerLiteralExpr -> sb.append(ex.asInt()).append("L")
is LongLiteralExpr -> sb.append(ex.asLong()).append("L")
is DoubleLiteralExpr -> sb.append(ex.asDouble())
+ is ArrayInitializerExpr -> {
+ sb.append("{")
+ ex.values.forEachLastAware { arrayElem, isLast ->
+ appendExpr(sb, arrayElem)
+ if (!isLast) sb.append(", ")
+ }
+ sb.append("}")
+ }
else -> sb.append(ex)
}
}
diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt
index fa2b41adcacb..ce83d3dc8e51 100755
--- a/tools/codegen/src/com/android/codegen/Main.kt
+++ b/tools/codegen/src/com/android/codegen/Main.kt
@@ -9,6 +9,7 @@ const val GENERATED_WARNING_PREFIX = "Code below generated by $CODEGEN_NAME"
const val INDENT_SINGLE = " "
val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean")
+val BOXED_PRIMITIVE_TYPES = PRIMITIVE_TYPES.map { it.capitalize() } - "Int" + "Integer" - "Char" + "Character"
val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern")
@@ -95,7 +96,13 @@ In addition, for any field mMyField(or myField) of type FieldType you can define
you can use with final fields.
Version: $CODEGEN_VERSION
-Questions? Feedback? Contact: eugenesusla@
+
+Questions? Feedback?
+Contact: eugenesusla@
+Bug/feature request: http://go/codegen-bug
+
+Slides: http://go/android-codegen
+In-depth example: http://go/SampleDataClass
"""
fun main(args: Array<String>) {
@@ -132,11 +139,15 @@ fun main(args: Array<String>) {
// $GENERATED_WARNING_PREFIX v$CODEGEN_VERSION.
//
// DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped
//
- // CHECKSTYLE:OFF Generated code
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
"""
if (FeatureFlag.CONST_DEFS()) generateConstDefs()
@@ -146,8 +157,7 @@ fun main(args: Array<String>) {
generateConstructor("public")
} else if (FeatureFlag.BUILDER()
|| FeatureFlag.COPY_CONSTRUCTOR()
- || FeatureFlag.WITHERS()
- || FeatureFlag.PARCELABLE()) {
+ || FeatureFlag.WITHERS()) {
generateConstructor("/* package-private */")
}
if (FeatureFlag.COPY_CONSTRUCTOR()) generateCopyConstructor()
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index b2cc81391510..3eb9e7bb68c6 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,7 +1,7 @@
package com.android.codegen
const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.1"
+const val CODEGEN_VERSION = "1.0.9"
const val CANONICAL_BUILDER_CLASS = "Builder"
const val BASE_BUILDER_CLASS = "BaseBuilder"
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index a1f068afa29a..e703397214eb 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -1,9 +1,6 @@
package com.android.codegen
-import com.github.javaparser.ast.Modifier
-import com.github.javaparser.ast.expr.AnnotationExpr
-import com.github.javaparser.ast.expr.Expression
-import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr
+import com.github.javaparser.ast.expr.*
import com.github.javaparser.ast.nodeTypes.NodeWithModifiers
import java.time.Instant
import java.time.ZoneId
@@ -88,3 +85,10 @@ fun abort(msg: String): Nothing {
}
fun bitAtExpr(bitIndex: Int) = "0x${java.lang.Long.toHexString(1L shl bitIndex)}"
+
+val AnnotationExpr.args: Map<String, Expression> get() = when (this) {
+ is MarkerAnnotationExpr -> emptyMap()
+ is SingleMemberAnnotationExpr -> mapOf("value" to memberValue)
+ is NormalAnnotationExpr -> pairs.map { it.name.asString() to it.value }.toMap()
+ else -> throw IllegalArgumentException("Unknown annotation expression: $this")
+}