From 94b01441d3f4261aeb01f67b15016f09fc21b842 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Sat, 3 Sep 2022 18:28:48 +0530 Subject: [PATCH] ClassFileReader: Implemented field reader --- .../classfile/ClassFileConstants.java | 31 +++ .../androidide/classfile/ClassFileReader.kt | 32 ++- .../androidide/classfile/ClassFileVersion.kt | 51 +++++ .../itsaky/androidide/classfile/IClassFile.kt | 9 +- .../itsaky/androidide/classfile/IFieldInfo.kt | 66 ++++++ .../classfile/attributes/Annotation.kt | 52 +++++ .../classfile/attributes/AnnotationAttr.kt | 40 ++++ .../classfile/attributes/BaseAttribute.kt | 25 +++ .../classfile/attributes/ConstantValueAttr.kt | 43 ++++ .../classfile/attributes/DeprecatedAttr.kt | 28 +++ .../classfile/attributes/ElementValue.kt | 140 ++++++++++++ .../classfile/attributes/IAttribute.kt | 106 +++++++++ .../classfile/attributes/MarkerAttribute.kt | 36 +++ .../RuntimeInvisibleAnnotationsAttr.kt | 31 +++ .../RuntimeInvisibleTypeAnnotations.kt | 37 +++ .../RuntimeVisibleAnnotationsAttr.kt | 30 +++ .../RuntimeVisibleTypeAnnotations.kt | 36 +++ .../classfile/attributes/SignatureAttr.kt | 42 ++++ .../classfile/attributes/SyntheticAttr.kt | 28 +++ .../classfile/attributes/TypeAnnotation.kt | 59 +++++ .../classfile/attributes/TypePath.kt | 54 +++++ .../classfile/attributes/targetInfos.kt | 210 ++++++++++++++++++ .../classfile/constants/Utf8Constant.kt | 5 +- .../classfile/internal/ClassFile.kt | 25 +-- .../classfile/internal/FieldInfo.kt | 37 +++ .../classfile/internal/NullAttribute.kt | 44 ++++ .../classfile/internal/NullField.kt | 39 ++++ .../classfile/ClassFileReaderTest.kt | 3 +- 28 files changed, 1302 insertions(+), 37 deletions(-) create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileVersion.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IFieldInfo.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/Annotation.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/AnnotationAttr.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/BaseAttribute.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ConstantValueAttr.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/DeprecatedAttr.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ElementValue.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/IAttribute.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/MarkerAttribute.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleAnnotationsAttr.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleTypeAnnotations.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleAnnotationsAttr.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleTypeAnnotations.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SignatureAttr.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SyntheticAttr.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypeAnnotation.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypePath.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/targetInfos.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/FieldInfo.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullAttribute.kt create mode 100644 build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullField.kt diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileConstants.java b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileConstants.java index adb97cb63..397143312 100644 --- a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileConstants.java +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileConstants.java @@ -53,4 +53,35 @@ public class ClassFileConstants { /** Is a module, not a class or interface. */ public static final int ACC_MODULE = 0x8000; + + public static final String ATTR_CONSTANT_VALUE = "ConstantValue"; + public static final String ATTR_CODE = "Code"; + public static final String ATTR_EXCEPTIONS = "Exceptions"; + public static final String ATTR_SOURCE_FILE = "SourceFile"; + public static final String ATTR_LINE_NUMBER_TABLE = "LineNumberTable"; + public static final String ATTR_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; + public static final String ATTR_INNER_CLASSES = "InnerClasses"; + public static final String ATTR_SYNTHETIC = "Synthetic"; + public static final String ATTR_DEPRECATED = "Deprecated"; + public static final String ATTR_ENCLOSING_METHOD = "EnclosingMethod"; + public static final String ATTR_SIGNATURE = "Signature"; + public static final String ATTR_SOURCE_DEBUG_EXTENSION = "SourceDebugExtension"; + public static final String ATTR_LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable"; + public static final String ATTR_RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; + public static final String ATTR_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; + public static final String ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; + public static final String ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; + public static final String ATTR_ANNOTATION_DEFAULT = "AnnotationDefault"; + public static final String ATTR_STACK_MAP_TABLE = "StackMapTable"; + public static final String ATTR_BOOTSTRAP_METHODS = "BootstrapMethods"; + public static final String ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations"; + public static final String ATTR_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations"; + public static final String ATTR_METHOD_PARAMETERS = "MethodParameters"; + public static final String ATTR_MODULE = "Module"; + public static final String ATTR_MODULE_PACKAGES = "ModulePackages"; + public static final String ATTR_MAIN_CLASS = "ModuleMainClass"; + public static final String ATTR_NEST_HOST = "NestHost"; + public static final String ATTR_NEST_MEMBERS = "NestMembers"; + public static final String ATTR_RECORD = "Record"; + public static final String ATTR_PERMITTED_SUBCLASSES = "PermittedSubclasses"; } diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileReader.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileReader.kt index ccf14be74..8f887f58c 100644 --- a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileReader.kt +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileReader.kt @@ -22,6 +22,7 @@ import com.itsaky.androidide.classfile.constants.IConstant.Type.DOUBLE import com.itsaky.androidide.classfile.constants.IConstant.Type.LONG import com.itsaky.androidide.classfile.internal.ClassFile import com.itsaky.androidide.classfile.internal.NullConstant +import com.itsaky.androidide.classfile.internal.NullField import java.io.DataInputStream import java.io.InputStream @@ -31,7 +32,7 @@ import java.io.InputStream * * @author Akash Yadav */ -class ClassFileReader(val stream: InputStream) { +class ClassFileReader(private val stream: InputStream) { /** * Read the class file. @@ -48,13 +49,15 @@ class ClassFileReader(val stream: InputStream) { } val file = ClassFile() - file.minorVersion = input.readUnsignedShort() - file.majorVersion = input.readUnsignedShort() - - if (file.majorVersion >= 56 && file.minorVersion != 0 && file.minorVersion != 65535) { + val minorVersion = input.readUnsignedShort() + val majorVersion = input.readUnsignedShort() + + if (majorVersion >= 56 && minorVersion != 0 && minorVersion != 65535) { // When major version is >= 56, minor version must be 0 or 65535 - throw IllegalStateException("Invalid class file minor version: '${file.minorVersion}'") + throw IllegalStateException("Invalid class file minor version: '${minorVersion}'") } + + file.version = ClassFileVersion.get(majorVersion, minorVersion) val poolCount = input.readUnsignedShort() file.constantPool = Array(poolCount) { NullConstant } @@ -63,27 +66,36 @@ class ClassFileReader(val stream: InputStream) { while (index < poolCount) { val constant = readPoolConstant(input) file.constantPool[index] = constant - + if (constant.type == LONG || constant.type == DOUBLE) { ++index } - + ++index } file.accessFlags = input.readUnsignedShort() file.thisClass = input.readUnsignedShort() file.superClass = input.readUnsignedShort() - + val interfaceCount = input.readUnsignedShort() file.interfaces = IntArray(interfaceCount) - + index = 0 while (index < interfaceCount) { file.interfaces[index] = input.readUnsignedShort() ++index } + val fieldCount = input.readUnsignedShort() + file.fields = Array(fieldCount) { NullField } + + index = 0 + while (index < fieldCount) { + file.fields[index] = IFieldInfo.read(input, file) + ++index + } + return file } diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileVersion.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileVersion.kt new file mode 100644 index 000000000..6c8bf822e --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileVersion.kt @@ -0,0 +1,51 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile + +/** + * Version info for the class file. + * + * @author Akash Yadav + */ +enum class ClassFileVersion(val major: Int, val minor: Int) { + JAVA_1_0(45, 0), + JAVA_1_1(45, 3), + JAVA_1_2(46, 0), + JAVA_1_3(47, 0), + JAVA_1_4(48, 0), + JAVA_5(49, 0), + JAVA_6(50, 0), + JAVA_7(51, 0), + JAVA_8(52, 0), + JAVA_9(53, 0), + JAVA_10(54, 0), + JAVA_11(55, 0), + JAVA_12(56, 0), + JAVA_13(57, 0), + JAVA_14(58, 0), + JAVA_15(59, 0), + JAVA_16(60, 0), + JAVA_17(61, 0); + + companion object { + internal fun get(major: Int, minor: Int): ClassFileVersion { + return values().firstOrNull { it.major == major && it.minor == minor } + ?: throw IllegalArgumentException("Unknown class file version '${major}.${minor}'") + } + } +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IClassFile.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IClassFile.kt index c670eaae9..40005d3e8 100644 --- a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IClassFile.kt +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IClassFile.kt @@ -25,11 +25,9 @@ import com.itsaky.androidide.classfile.constants.IConstant * @author Akash Yadav */ interface IClassFile { - /** Major version of the class file. */ - val majorVersion: Int - /** Minor version of the class file. */ - val minorVersion: Int + /** The class file version. */ + val version: ClassFileVersion /** The constant pool. */ val constantPool: Array @@ -52,6 +50,9 @@ interface IClassFile { /** The interfaces that this class implements. */ val interfaces: IntArray + /** The fields of this class. */ + val fields: Array + /** Get the string representation of this class' name. */ fun getName(): String diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IFieldInfo.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IFieldInfo.kt new file mode 100644 index 000000000..16497a0a0 --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IFieldInfo.kt @@ -0,0 +1,66 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile + +import com.itsaky.androidide.classfile.attributes.IAttribute +import com.itsaky.androidide.classfile.internal.ClassFile +import com.itsaky.androidide.classfile.internal.FieldInfo +import com.itsaky.androidide.classfile.internal.NullAttribute +import java.io.DataInputStream + +/** + * Information about a field. + * + * @author Akash Yadav + */ +interface IFieldInfo { + + /** Access flags for this field. */ + val accessFlags: Int + + /** The index in the constant pool which corresponds to the name of this field. */ + val nameIndex: Int + + /** + * The index in the constant pool which corresponds to the field descriptor string for this field. + */ + val descriptorIndex: Int + + /** The attributes of this field. */ + val attributes: Array + + companion object { + + internal fun read(input: DataInputStream, file: ClassFile): IFieldInfo { + val field = FieldInfo() + field.accessFlags = input.readUnsignedShort() + field.nameIndex = input.readUnsignedShort() + field.descriptorIndex = input.readUnsignedShort() + + val attrCount = input.readUnsignedShort() + field.attributes = Array(attrCount) { NullAttribute } + + var index = 0 + while (index < attrCount) { + field.attributes[index] = IAttribute.read(input, file) + ++index + } + return field + } + } +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/Annotation.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/Annotation.kt new file mode 100644 index 000000000..36dc134ce --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/Annotation.kt @@ -0,0 +1,52 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import java.io.DataInputStream + +/** + * Annotation model for annotation based attributes in class files. + * + * @author Akash Yadav + */ +open class Annotation { + + /** + * Index of the entry in the constant pool which corresponds to the type of the the annotation. + * The entry at this index is always an UTF-8 constant. + */ + var typeIndex: Int = -1 + internal set + + /** The element value pairs. */ + open var elementValues: Array> = emptyArray() + internal set + + companion object { + internal fun read(input: DataInputStream): Annotation { + val annotation = Annotation() + annotation.typeIndex = input.readUnsignedShort() + + val valueCount = input.readUnsignedShort() + annotation.elementValues = + Array(valueCount) { input.readUnsignedShort() to ElementValue.read(input) } + + return annotation + } + } +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/AnnotationAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/AnnotationAttr.kt new file mode 100644 index 000000000..f193e0f2e --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/AnnotationAttr.kt @@ -0,0 +1,40 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import com.itsaky.androidide.classfile.ClassFileVersion +import com.itsaky.androidide.classfile.ClassFileVersion.JAVA_5 +import com.itsaky.androidide.classfile.IClassFile +import java.io.DataInputStream + +/** + * An attribute which stores information about annotations. + * + * @author Akash Yadav + */ +abstract class AnnotationAttr(nameIndex: Int) : BaseAttribute(nameIndex) { + override val version: ClassFileVersion = JAVA_5 + + /** The annotations. */ + var annotations: Array = emptyArray() + + override fun read(attrLength: Int, file: IClassFile, input: DataInputStream) { + val count = input.readUnsignedShort() + this.annotations = Array(count) { Annotation.read(input) } + } +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/BaseAttribute.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/BaseAttribute.kt new file mode 100644 index 000000000..3350e8700 --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/BaseAttribute.kt @@ -0,0 +1,25 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +/** + * Base class for attrbutes. + * + * @author Akash Yadav + */ +abstract class BaseAttribute(override val nameIndex: Int): IAttribute \ No newline at end of file diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ConstantValueAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ConstantValueAttr.kt new file mode 100644 index 000000000..d18075f7d --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ConstantValueAttr.kt @@ -0,0 +1,43 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import com.itsaky.androidide.classfile.ClassFileVersion +import com.itsaky.androidide.classfile.ClassFileVersion.JAVA_1_2 +import com.itsaky.androidide.classfile.IClassFile +import com.itsaky.androidide.classfile.attributes.IAttribute.Type +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.CONSTANT_VALUE +import java.io.DataInputStream + +/** + * Constant value for a field. + * + * @author Akash Yadav + */ +class ConstantValueAttr(nameIndex: Int) : BaseAttribute(nameIndex) { + override val version: ClassFileVersion = JAVA_1_2 + override val type: Type = CONSTANT_VALUE + + /** The index in the constant pool which corresponds to the value of this constant. */ + var valueIndex: Int = -1 + internal set + + override fun read(attrLength: Int, file: IClassFile, input: DataInputStream) { + this.valueIndex = input.readUnsignedShort() + } +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/DeprecatedAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/DeprecatedAttr.kt new file mode 100644 index 000000000..e9e67e5bd --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/DeprecatedAttr.kt @@ -0,0 +1,28 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import com.itsaky.androidide.classfile.ClassFileVersion.JAVA_1_1 +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.DEPRECATED + +/** + * Indicates that a class, field or method is deprecated. + * + * @author Akash Yadav + */ +class DeprecatedAttr(nameIndex: Int) : MarkerAttribute(nameIndex, DEPRECATED, JAVA_1_1) diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ElementValue.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ElementValue.kt new file mode 100644 index 000000000..ebb1d0cff --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ElementValue.kt @@ -0,0 +1,140 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import com.itsaky.androidide.classfile.attributes.ElementValue.Type.ANNOTATION_INTERFACE +import com.itsaky.androidide.classfile.attributes.ElementValue.Type.ARRAY_TYPE +import com.itsaky.androidide.classfile.attributes.ElementValue.Type.CLASS +import com.itsaky.androidide.classfile.attributes.ElementValue.Type.ENUM_CLASS +import com.itsaky.androidide.classfile.attributes.ElementValue.Type.UNKNOWN +import java.io.DataInputStream + +/** + * The element value in an annotation. + * + * @author Akash Yadav + */ +class ElementValue { + + /** The element tag. */ + var type: Type = UNKNOWN + internal set + + /** + * The constant value if the [type] is one of the following : + * + * - [Type.BYTE] + * - [Type.CHAR] + * - [Type.DOUBLE] + * - [Type.FLOAT] + * - [Type.INT] + * - [Type.LONG] + * - [Type.SHORT] + * - [Type.BOOLEAN] + * - [Type.STRING] + */ + var constValIndex: Int = -1 + internal set + + /** The element constant value if the [type] is [Type.ENUM_CLASS]. */ + var enumValue: EnumConstValue? = null + internal set + + /** The class name index if the [type] is [Type.CLASS]. */ + var classNameIndex: Int = -1 + internal set + + /** The annotation constant value if the [type] is [Type.ANNOTATION_INTERFACE]. */ + var annotationValue: Annotation? = null + internal set + + /** The array constant value if the [type] is [Type.ARRAY_TYPE]. */ + var arrayValue: Array? = null + internal set + + companion object { + internal fun read(input: DataInputStream): ElementValue { + val value = ElementValue() + value.type = Type.forTag(input.readUnsignedByte()) + when (value.type) { + ENUM_CLASS -> value.enumValue = EnumConstValue.read(input) + CLASS -> value.classNameIndex = input.readUnsignedShort() + ANNOTATION_INTERFACE -> value.annotationValue = Annotation.read(input) + ARRAY_TYPE -> { + val count = input.readUnsignedShort() + val nullElem = ElementValue() + value.arrayValue = Array(count) { nullElem } + + var index = 0 + while (index < count) { + value.arrayValue!![index] = read(input) + ++index + } + } + else -> value.constValIndex = input.readUnsignedShort() + } + + return value + } + } + + /** + * The enum constant value. + * + * @property typeNameIndex The index of the entry in the constant pool which corresponds to the + * name of the type of this constant. + * @property constNameIndex The index of the entry in the constant pool which corresponds to the + * name of this constant. + */ + data class EnumConstValue(val typeNameIndex: Int, val constNameIndex: Int) { + companion object { + internal fun read(input: DataInputStream): EnumConstValue { + return EnumConstValue(input.readUnsignedShort(), input.readUnsignedShort()) + } + } + } + + /** The type of [ElementValue]. */ + enum class Type(val tag: Int) { + BYTE('B'.code), + CHAR('C'.code), + DOUBLE('D'.code), + FLOAT('F'.code), + INT('I'.code), + LONG('J'.code), + SHORT('S'.code), + BOOLEAN('Z'.code), + STRING('s'.code), + ENUM_CLASS('e'.code), + CLASS('c'.code), + ANNOTATION_INTERFACE('@'.code), + ARRAY_TYPE('['.code), + + /** **FOR INTERNAL USE ONLY** */ + UNKNOWN('-'.code); + + companion object { + + /** Get the element type for the given tag value. */ + internal fun forTag(tag: Int): Type { + return values().firstOrNull { it.tag == tag } + ?: throw IllegalArgumentException("Invalid tag for element value type tag '$tag'") + } + } + } +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/IAttribute.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/IAttribute.kt new file mode 100644 index 000000000..d3e142ee1 --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/IAttribute.kt @@ -0,0 +1,106 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_CONSTANT_VALUE +import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_DEPRECATED +import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS +import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS +import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_RUNTIME_VISIBLE_ANNOTATIONS +import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS +import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_SIGNATURE +import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_SYNTHETIC +import com.itsaky.androidide.classfile.ClassFileVersion +import com.itsaky.androidide.classfile.IClassFile +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.CONSTANT_VALUE +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.DEPRECATED +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_INVISIBLE_ANNOTATIONS +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_VISIBLE_ANNOTATIONS +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_VISIBLE_TYPE_ANNOTATIONS +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.SIGNATURE +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.SYNTHETIC +import com.itsaky.androidide.classfile.constants.Utf8Constant +import com.itsaky.androidide.classfile.internal.ClassFile +import java.io.DataInputStream + +/** + * Base class for all attributes in a class, field or method. + * + * @author Akash Yadav + */ +interface IAttribute { + + /** The version of the class file in which this attribute appears. */ + val version: ClassFileVersion + + /** The index of the entry in the constant pool which represents the name of this attrbute. */ + val nameIndex: Int + + /** The type of attribute. */ + val type: Type + + /** + * **FOR INTERNAL USE ONLY** + * + * Read further information from the given input. + */ + fun read(attrLength: Int, file: IClassFile, input: DataInputStream) + + enum class Type(val attrName: String) { + CONSTANT_VALUE(ATTR_CONSTANT_VALUE), + SYNTHETIC(ATTR_SYNTHETIC), + SIGNATURE(ATTR_SIGNATURE), + DEPRECATED(ATTR_DEPRECATED), + RUNTIME_VISIBLE_ANNOTATIONS(ATTR_RUNTIME_VISIBLE_ANNOTATIONS), + RUNTIME_INVISIBLE_ANNOTATIONS(ATTR_RUNTIME_INVISIBLE_ANNOTATIONS), + RUNTIME_VISIBLE_TYPE_ANNOTATIONS(ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS), + RUNTIME_INVISIBLE_TYPE_ANNOTATIONS(ATTR_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); + + companion object { + internal fun forName(name: String): Type { + return values().firstOrNull { it.attrName == name } + ?: throw IllegalArgumentException("Unknown attribute '$name'") + } + } + } + + companion object { + internal fun read(input: DataInputStream, file: ClassFile): IAttribute { + val nameIndex = input.readUnsignedShort() + val attrLength = input.readInt() + val name = (file.constantPool[nameIndex] as Utf8Constant).content() + val type = Type.forName(name) + return createForType(type, nameIndex).also { it.read(attrLength, file, input) } + } + + private fun createForType(type: Type, nameIndex: Int): IAttribute { + return when (type) { + CONSTANT_VALUE -> ConstantValueAttr(nameIndex) + SYNTHETIC -> SyntheticAttr(nameIndex) + SIGNATURE -> SignatureAttr(nameIndex) + DEPRECATED -> DeprecatedAttr(nameIndex) + RUNTIME_VISIBLE_ANNOTATIONS -> RuntimeVisibleAnnotationsAttr(nameIndex) + RUNTIME_INVISIBLE_ANNOTATIONS -> RuntimeVisibleAnnotationsAttr(nameIndex) + RUNTIME_VISIBLE_TYPE_ANNOTATIONS -> RuntimeVisibleTypeAnnotations(nameIndex) + RUNTIME_INVISIBLE_TYPE_ANNOTATIONS -> RuntimeInvisibleTypeAnnotations(nameIndex) + else -> throw IllegalArgumentException("Unknown attribute type $type") + } + } + } +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/MarkerAttribute.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/MarkerAttribute.kt new file mode 100644 index 000000000..d331d6ca1 --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/MarkerAttribute.kt @@ -0,0 +1,36 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import com.itsaky.androidide.classfile.ClassFileVersion +import com.itsaky.androidide.classfile.IClassFile +import com.itsaky.androidide.classfile.attributes.IAttribute.Type +import java.io.DataInputStream + +/** + * An attribute which is simply a marker and does not contain any further information. + * + * @author Akash Yadav + */ +abstract class MarkerAttribute( + nameIndex: Int, + override val type: Type, + override val version: ClassFileVersion +) : BaseAttribute(nameIndex) { + override fun read(attrLength: Int, file: IClassFile, input: DataInputStream) {} +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleAnnotationsAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleAnnotationsAttr.kt new file mode 100644 index 000000000..9bb836802 --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleAnnotationsAttr.kt @@ -0,0 +1,31 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import com.itsaky.androidide.classfile.attributes.IAttribute.Type +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_INVISIBLE_ANNOTATIONS + +/** + * Stores run-time invisible annotations on the declaration of the corresponding class, method, + * field, or record component. + * + * @author Akash Yadav + */ +class RuntimeInvisibleAnnotationsAttr(nameIndex: Int) : AnnotationAttr(nameIndex) { + override val type: Type = RUNTIME_INVISIBLE_ANNOTATIONS +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleTypeAnnotations.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleTypeAnnotations.kt new file mode 100644 index 000000000..62cc6419b --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleTypeAnnotations.kt @@ -0,0 +1,37 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import com.itsaky.androidide.classfile.attributes.IAttribute.Type +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_VISIBLE_TYPE_ANNOTATIONS + +/** + * Stores run-time invisible annotations on type parameter declarations of generic classes, + * interfaces, methods, and constructors. + * + * @author Akash Yadav + */ +class RuntimeInvisibleTypeAnnotations(nameIndex: Int) : AnnotationAttr(nameIndex) { + override val type: Type = RUNTIME_INVISIBLE_TYPE_ANNOTATIONS + + /** + * The annotations always of type [TypeAnnotation] + */ + var typeAnnotations: Array = emptyArray() +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleAnnotationsAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleAnnotationsAttr.kt new file mode 100644 index 000000000..c283af1c8 --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleAnnotationsAttr.kt @@ -0,0 +1,30 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import com.itsaky.androidide.classfile.attributes.IAttribute.Type +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_VISIBLE_ANNOTATIONS + +/** + * Stores run-time visible annotations on the declaration of the corresponding class, field, method, + * or record component. + * @author Akash Yadav + */ +class RuntimeVisibleAnnotationsAttr(nameIndex: Int) : AnnotationAttr(nameIndex) { + override val type: Type = RUNTIME_VISIBLE_ANNOTATIONS +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleTypeAnnotations.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleTypeAnnotations.kt new file mode 100644 index 000000000..6a93da5eb --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleTypeAnnotations.kt @@ -0,0 +1,36 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import com.itsaky.androidide.classfile.attributes.IAttribute.Type +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_VISIBLE_TYPE_ANNOTATIONS + +/** + * Stores run-time visible annotations on type parameter declarations of generic classes, + * interfaces, methods, and constructors. + * + * @author Akash Yadav + */ +class RuntimeVisibleTypeAnnotations(nameIndex: Int) : AnnotationAttr(nameIndex) { + override val type: Type = RUNTIME_VISIBLE_TYPE_ANNOTATIONS + + /** + * The annotations always of type [TypeAnnotation] + */ + var typeAnnotations: Array = emptyArray() +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SignatureAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SignatureAttr.kt new file mode 100644 index 000000000..005e6a69b --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SignatureAttr.kt @@ -0,0 +1,42 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import com.itsaky.androidide.classfile.ClassFileVersion +import com.itsaky.androidide.classfile.ClassFileVersion.JAVA_5 +import com.itsaky.androidide.classfile.IClassFile +import com.itsaky.androidide.classfile.attributes.IAttribute.Type +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.SIGNATURE +import java.io.DataInputStream + +/** + * Signature of a class, field or method. + * + * @author Akash Yadav + */ +class SignatureAttr(nameIndex: Int) : BaseAttribute(nameIndex) { + override val version: ClassFileVersion = JAVA_5 + override val type: Type = SIGNATURE + + var signatureIndex: Int = -1 + internal set + + override fun read(attrLength: Int, file: IClassFile, input: DataInputStream) { + this.signatureIndex = input.readUnsignedShort() + } +} \ No newline at end of file diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SyntheticAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SyntheticAttr.kt new file mode 100644 index 000000000..947744f1e --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SyntheticAttr.kt @@ -0,0 +1,28 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import com.itsaky.androidide.classfile.ClassFileVersion.JAVA_1_1 +import com.itsaky.androidide.classfile.attributes.IAttribute.Type.SYNTHETIC + +/** + * Indicates that a class, field or method is synthetic. + * + * @author Akash Yadav + */ +class SyntheticAttr(nameIndex: Int) : MarkerAttribute(nameIndex, SYNTHETIC, JAVA_1_1) diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypeAnnotation.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypeAnnotation.kt new file mode 100644 index 000000000..388d470dd --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypeAnnotation.kt @@ -0,0 +1,59 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import java.io.DataInputStream + +/** + * Annotation model for (in)visible type annotations. + * + * @author Akash Yadav + */ +class TypeAnnotation { + + lateinit var targetInfo: TargetInfo + internal set + + lateinit var typePath: TypePath + internal set + + /** + * Index of the entry in the constant pool which corresponds to the type of the the annotation. + * The entry at this index is always an UTF-8 constant. + */ + var typeIndex: Int = -1 + internal set + + /** The element value pairs. */ + var elementValues: Array> = emptyArray() + internal set + + companion object { + internal fun read(input: DataInputStream): TypeAnnotation { + val annotation = TypeAnnotation() + val targetType = input.readUnsignedByte() + annotation.targetInfo = TargetInfo.forType(targetType).apply { read(input) } + annotation.typePath = TypePath().apply { read(input) } + annotation.typeIndex = input.readUnsignedShort() + val valueCount = input.readUnsignedShort() + annotation.elementValues = + Array(valueCount) { input.readUnsignedShort() to ElementValue.read(input) } + return annotation + } + } +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypePath.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypePath.kt new file mode 100644 index 000000000..f65622c02 --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypePath.kt @@ -0,0 +1,54 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import java.io.DataInputStream + +/** + * Wherever a type is used in a declaration or expression, the type_path structure identifies which + * part of the type is annotated. An annotation may appear on the type itself, but if the type is a + * reference type, then there are additional locations where an annotation may appear. + * + * @author Akash Yadav + */ +class TypePath { + + var paths: Array = emptyArray() + internal set + + class PathEntry { + var typePathKind: Int = -1 + internal set + var typeArgumentIndex: Int = -1 + internal set + + internal fun read(input: DataInputStream) { + this.typePathKind = input.readUnsignedByte() + this.typeArgumentIndex = input.readUnsignedByte() + } + } + + internal fun read(input: DataInputStream) { + this.paths = + Array(input.readUnsignedByte()) { + val entry = PathEntry() + entry.read(input) + entry + } + } +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/targetInfos.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/targetInfos.kt new file mode 100644 index 000000000..e444b01c4 --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/targetInfos.kt @@ -0,0 +1,210 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.attributes + +import java.io.DataInputStream + +/** + * Target information in a type annotation. + * + * @author Akash Yadav + */ +abstract class TargetInfo(val currentType: Int, val types: IntArray) { + + /** The read the target info. */ + internal abstract fun read(input: DataInputStream) + + companion object { + + internal fun forType(type: Int): TargetInfo { + return when (type) { + 0x00, + 0x01 -> TypeParameterTarget(type) + 0x10 -> SupertypeTarget(type) + 0x11, + 0x12 -> TypeParameterBoundTarget(type) + 0x13, + 0x14, + 0x15 -> EmptyTarget(type) + 0x16 -> FormalParameterTarget(type) + 0x17 -> ThrowsTarget(type) + 0x40, + 0x41 -> LocalVarTarget(type) + 0x42 -> CatchTarget(type) + 0x43, + 0x44, + 0x45, + 0x46 -> OffsetTarget(type) + 0x47, + 0x48, + 0x49, + 0x4A, + 0x4B -> TypeArgumentTarget(type) + else -> throw IllegalArgumentException("Unknown type parameter target") + } + } + } +} + +open class TypeParameterTarget +@JvmOverloads +constructor(currentType: Int, types: IntArray = intArrayOf(0x00, 0x01)) : + TargetInfo(currentType, types) { + + /** + * The value of the type_parameter_index item specifies which type parameter declaration is + * annotated. A type_parameter_index value of 0 specifies the first type parameter declaration. + */ + var typeParameterIndex: Int = -1 + internal set + + override fun read(input: DataInputStream) { + this.typeParameterIndex = input.readUnsignedByte() + } +} + +class SupertypeTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x10)) { + + /** + * A supertype_index value of 65535 specifies that the annotation appears on the superclass in an + * extends clause of a class declaration. + * + * Any other supertype_index value is an index into the interfaces array of the enclosing + * ClassFile structure, and specifies that the annotation appears on that superinterface in either + * the implements clause of a class declaration or the extends clause of an interface declaration. + */ + var supertypeIndex: Int = -1 + internal set + + override fun read(input: DataInputStream) { + this.supertypeIndex = input.readUnsignedShort() + } +} + +class TypeParameterBoundTarget(currentType: Int) : + TypeParameterTarget(currentType, intArrayOf(0x11, 0x12)) { + + /** + * The value of the bound_index item specifies which bound of the type parameter declaration + * indicated by type_parameter_index is annotated. A bound_index value of 0 specifies the first + * bound of a type parameter declaration. + */ + var boundIndex: Int = -1 + internal set + + override fun read(input: DataInputStream) { + super.read(input) + this.boundIndex = input.readUnsignedShort() + } +} + +class EmptyTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x13, 0x14, 0x15)) { + + override fun read(input: DataInputStream) {} +} + +class FormalParameterTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x16)) { + + /** + * The value of the formal_parameter_index item specifies which formal parameter declaration has + * an annotated type. A formal_parameter_index value of i may, but is not required to, correspond + * to the i'th parameter descriptor in the method descriptor. + */ + var formalParameterIndex: Int = -1 + internal set + + override fun read(input: DataInputStream) { + this.formalParameterIndex = input.readUnsignedByte() + } +} + +class ThrowsTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x17)) { + + /** + * The value of the throws_type_index item is an index into the exception_index_table array of the + * Exceptions attribute of the method_info structure enclosing the RuntimeVisibleTypeAnnotations + * attribute. + */ + var throwsTypeIndex: Int = -1 + internal set + + override fun read(input: DataInputStream) { + this.throwsTypeIndex = input.readUnsignedShort() + } +} + +class LocalVarTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x40, 0x41)) { + + var table: Array = emptyArray() + + class LocalVarTagetTableEntry { + var startPc: Int = -1 + internal set + var length: Int = -1 + internal set + var index: Int = -1 + internal set + } + + override fun read(input: DataInputStream) { + val length = input.readUnsignedShort() + this.table = + Array(length) { + val entry = LocalVarTagetTableEntry() + entry.startPc = input.readUnsignedShort() + entry.length = input.readUnsignedShort() + entry.index = input.readUnsignedShort() + entry + } + } +} + +class CatchTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x42)) { + + var exceptionTableIndex: Int = -1 + internal set + + override fun read(input: DataInputStream) { + this.exceptionTableIndex = input.readUnsignedShort() + } +} + +class OffsetTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x43, 0x44, 0x45, 0x46)) { + + var offset: Int = -1 + internal set + + override fun read(input: DataInputStream) { + this.offset = input.readUnsignedShort() + } +} + +class TypeArgumentTarget(currentType: Int) : + TargetInfo(currentType, intArrayOf(0x47, 0x48, 0x49, 0x4A, 0x4B)) { + + var offset: Int = -1 + internal set + + var typeArgumentIndex: Int = -1 + internal set + + override fun read(input: DataInputStream) { + this.offset = input.readUnsignedShort() + this.typeArgumentIndex = input.readUnsignedByte() + } +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/constants/Utf8Constant.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/constants/Utf8Constant.kt index e086f2bc2..79e2ccf7f 100644 --- a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/constants/Utf8Constant.kt +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/constants/Utf8Constant.kt @@ -33,6 +33,9 @@ class Utf8Constant : IConstant { var bytes: ByteArray = ByteArray(0) private set + /** Get the string represented by this constant. */ + fun content() = this.bytes.decodeToString() + override fun read(input: DataInputStream) { val length = input.readUnsignedShort() this.bytes = ByteArray(length) @@ -56,6 +59,6 @@ class Utf8Constant : IConstant { } override fun toString(): String { - return "Utf8Constant(type=$type, bytes=${bytes.contentToString()}, bytesAsString=${bytes.decodeToString()})" + return "Utf8Constant(type=$type, bytes=${bytes.contentToString()}, bytesAsString=${content()})" } } diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/ClassFile.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/ClassFile.kt index 9d532bbad..f63b83ea2 100644 --- a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/ClassFile.kt +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/ClassFile.kt @@ -17,8 +17,11 @@ package com.itsaky.androidide.classfile.internal +import com.itsaky.androidide.classfile.ClassFileVersion +import com.itsaky.androidide.classfile.ClassFileVersion.JAVA_1_0 import com.itsaky.androidide.classfile.constants.IConstant import com.itsaky.androidide.classfile.IClassFile +import com.itsaky.androidide.classfile.IFieldInfo import com.itsaky.androidide.classfile.constants.ClassConstant import com.itsaky.androidide.classfile.constants.Utf8Constant @@ -29,31 +32,13 @@ import com.itsaky.androidide.classfile.constants.Utf8Constant */ internal class ClassFile : IClassFile { - /** Major version of the class file. */ - override var majorVersion: Int = 0 - - /** Minor version of the class file. */ - override var minorVersion: Int = 0 - - /** The constant pool. */ + override var version: ClassFileVersion = JAVA_1_0 override var constantPool: Array = emptyArray() - - /** Access flags for the class. */ override var accessFlags: Int = 0 - - /** - * The index in the constant pool which corresponds to the internal representation of the name of - * this class. - */ override var thisClass: Int = -1 - - /** - * The index in the constant pool which corresponds to the internal representation of the name of - * the super class of this class. - */ override var superClass: Int = -1 - override var interfaces: IntArray = IntArray(0) + override var fields: Array = emptyArray() override fun getName(): String { val entry = constantPool[thisClass] diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/FieldInfo.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/FieldInfo.kt new file mode 100644 index 000000000..c9c52c692 --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/FieldInfo.kt @@ -0,0 +1,37 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.internal + +import com.itsaky.androidide.classfile.IFieldInfo +import com.itsaky.androidide.classfile.attributes.IAttribute + +/** + * Default internal implementation of [IFieldInfo]. + * + * @author Akash Yadav + */ +internal class FieldInfo : IFieldInfo { + override var accessFlags: Int = 0 + internal set + override var nameIndex: Int = -1 + internal set + override var descriptorIndex: Int = -1 + internal set + override var attributes: Array = emptyArray() + internal set +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullAttribute.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullAttribute.kt new file mode 100644 index 000000000..73d3c3356 --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullAttribute.kt @@ -0,0 +1,44 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.internal + +import com.itsaky.androidide.classfile.ClassFileVersion +import com.itsaky.androidide.classfile.IClassFile +import com.itsaky.androidide.classfile.attributes.IAttribute +import com.itsaky.androidide.classfile.attributes.IAttribute.Type +import java.io.DataInputStream + +/** + * **FOR INTERNAL USE ONLY** + * + * Represents a null attribute. Not present in a class file. + * + * @author Akash Yadav + */ +internal object NullAttribute : IAttribute { + override val version: ClassFileVersion + get() = throw UnsupportedOperationException() + override val nameIndex: Int + get() = throw UnsupportedOperationException() + override val type: Type + get() = throw UnsupportedOperationException() + + override fun read(attrLength: Int, file: IClassFile, input: DataInputStream) { + throw UnsupportedOperationException() + } +} diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullField.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullField.kt new file mode 100644 index 000000000..6d4510442 --- /dev/null +++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullField.kt @@ -0,0 +1,39 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.classfile.internal + +import com.itsaky.androidide.classfile.IFieldInfo +import com.itsaky.androidide.classfile.attributes.IAttribute + +/** + * **FOR INTERNAL USE ONLY** + * + * Represents a null field. Not available in class file. + * + * @author Akash Yadav + */ +internal object NullField : IFieldInfo { + override val accessFlags: Int + get() = throw UnsupportedOperationException() + override val nameIndex: Int + get() = throw UnsupportedOperationException() + override val descriptorIndex: Int + get() = throw UnsupportedOperationException() + override val attributes: Array + get() = throw UnsupportedOperationException() +} diff --git a/build-tools/classfile/src/test/java/com/itsaky/androidide/classfile/ClassFileReaderTest.kt b/build-tools/classfile/src/test/java/com/itsaky/androidide/classfile/ClassFileReaderTest.kt index 95be69804..655cc45e8 100644 --- a/build-tools/classfile/src/test/java/com/itsaky/androidide/classfile/ClassFileReaderTest.kt +++ b/build-tools/classfile/src/test/java/com/itsaky/androidide/classfile/ClassFileReaderTest.kt @@ -39,8 +39,7 @@ class ClassFileReaderTest { val file = reader.read() assertThat(file).isNotNull() - assertThat(file.majorVersion).isEqualTo(55) - assertThat(file.minorVersion).isEqualTo(0) + assertThat(file.version).isEqualTo(ClassFileVersion.JAVA_11) assertThat(file.getName()).isEqualTo("io/github/rosemoe/sora/widget/CodeEditor") assertThat(file.getSuperClassName()).isEqualTo("android/view/View")