diff options
author | Steven Laver <lavers@google.com> | 2019-11-05 13:42:59 -0800 |
---|---|---|
committer | Steven Laver <lavers@google.com> | 2019-11-09 01:16:30 +0000 |
commit | 7c6cc72e18cc1df5205fd2bc47664e6cc2534ad2 (patch) | |
tree | fc34e4ad6037cf231cccc3b56ccd13e82917520a /tools/codegen/src | |
parent | 8f4f93bf3ba75d8e83cb0a8618cb80f226ada5ac (diff) | |
parent | da5e1bd24a9a0ca24e7e49fad9e604409e573376 (diff) |
Merge RP1A.191024.001
Change-Id: I5cda3bba276e99d948b752be87d4599e9f882e0f
Diffstat (limited to 'tools/codegen/src')
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") +} |